diff --git a/Cargo.lock b/Cargo.lock
index 347976691d..a185542c63 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -102,14 +102,20 @@ dependencies = [
"anyhow",
"chrono",
"collections",
+ "ctor",
"editor",
+ "env_logger 0.9.3",
"fs",
"futures 0.3.28",
"gpui",
+ "indoc",
"isahc",
"language",
+ "log",
"menu",
+ "ordered-float",
"project",
+ "rand 0.8.5",
"regex",
"schemars",
"search",
@@ -1447,7 +1453,7 @@ dependencies = [
[[package]]
name = "collab"
-version = "0.18.0"
+version = "0.19.0"
dependencies = [
"anyhow",
"async-tungstenite",
@@ -2762,6 +2768,7 @@ dependencies = [
"smol",
"sum_tree",
"tempfile",
+ "text",
"time 0.3.27",
"util",
]
@@ -4170,8 +4177,7 @@ dependencies = [
[[package]]
name = "lsp-types"
version = "0.94.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c66bfd44a06ae10647fe3f8214762e9369fd4248df1350924b4ef9e770a85ea1"
+source = "git+https://github.com/zed-industries/lsp-types?branch=updated-completion-list-item-defaults#90a040a1d195687bd19e1df47463320a44e93d7a"
dependencies = [
"bitflags 1.3.2",
"serde",
@@ -5649,6 +5655,7 @@ dependencies = [
name = "quick_action_bar"
version = "0.1.0"
dependencies = [
+ "ai",
"editor",
"gpui",
"search",
@@ -7629,7 +7636,6 @@ dependencies = [
"ctor",
"digest 0.9.0",
"env_logger 0.9.3",
- "fs",
"gpui",
"lazy_static",
"log",
@@ -9695,7 +9701,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.102.0"
+version = "0.103.0"
dependencies = [
"activity_indicator",
"ai",
diff --git a/assets/icons/check_circle.svg b/assets/icons/check_circle.svg
index 85ba2e1f37..b48fe34631 100644
--- a/assets/icons/check_circle.svg
+++ b/assets/icons/check_circle.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/error.svg b/assets/icons/error.svg
index 82b9401d08..593629beee 100644
--- a/assets/icons/error.svg
+++ b/assets/icons/error.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/warning.svg b/assets/icons/warning.svg
index 6b3d0fd41e..e581def0d0 100644
--- a/assets/icons/warning.svg
+++ b/assets/icons/warning.svg
@@ -1,5 +1,6 @@
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index a1c771da02..1b2d8ce419 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -522,7 +522,7 @@
// TODO: Move this to a dock open action
"cmd-shift-c": "collab_panel::ToggleFocus",
"cmd-alt-i": "zed::DebugElements",
- "ctrl-shift-:": "editor::ToggleInlayHints",
+ "ctrl-:": "editor::ToggleInlayHints",
}
},
{
@@ -530,7 +530,8 @@
"bindings": {
"alt-enter": "editor::OpenExcerpts",
"cmd-f8": "editor::GoToHunk",
- "cmd-shift-f8": "editor::GoToPrevHunk"
+ "cmd-shift-f8": "editor::GoToPrevHunk",
+ "ctrl-enter": "assistant::InlineAssist"
}
},
{
diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml
index 013565e14f..4438f88108 100644
--- a/crates/ai/Cargo.toml
+++ b/crates/ai/Cargo.toml
@@ -24,7 +24,9 @@ workspace = { path = "../workspace" }
anyhow.workspace = true
chrono = { version = "0.4", features = ["serde"] }
futures.workspace = true
+indoc.workspace = true
isahc.workspace = true
+ordered-float.workspace = true
regex.workspace = true
schemars.workspace = true
serde.workspace = true
@@ -35,3 +37,8 @@ tiktoken-rs = "0.4"
[dev-dependencies]
editor = { path = "../editor", features = ["test-support"] }
project = { path = "../project", features = ["test-support"] }
+
+ctor.workspace = true
+env_logger.workspace = true
+log.workspace = true
+rand.workspace = true
diff --git a/crates/ai/src/ai.rs b/crates/ai/src/ai.rs
index d2be651bd5..2c2d7e774e 100644
--- a/crates/ai/src/ai.rs
+++ b/crates/ai/src/ai.rs
@@ -1,28 +1,33 @@
pub mod assistant;
mod assistant_settings;
+mod streaming_diff;
-use anyhow::Result;
+use anyhow::{anyhow, Result};
pub use assistant::AssistantPanel;
use assistant_settings::OpenAIModel;
use chrono::{DateTime, Local};
use collections::HashMap;
use fs::Fs;
-use futures::StreamExt;
-use gpui::AppContext;
+use futures::{io::BufReader, AsyncBufReadExt, AsyncReadExt, Stream, StreamExt};
+use gpui::{executor::Background, AppContext};
+use isahc::{http::StatusCode, Request, RequestExt};
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{
cmp::Reverse,
ffi::OsStr,
fmt::{self, Display},
+ io,
path::PathBuf,
sync::Arc,
};
use util::paths::CONVERSATIONS_DIR;
+const OPENAI_API_URL: &'static str = "https://api.openai.com/v1";
+
// Data types for chat completion requests
#[derive(Debug, Serialize)]
-struct OpenAIRequest {
+pub struct OpenAIRequest {
model: String,
messages: Vec,
stream: bool,
@@ -116,7 +121,7 @@ struct RequestMessage {
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
-struct ResponseMessage {
+pub struct ResponseMessage {
role: Option,
content: Option,
}
@@ -150,7 +155,7 @@ impl Display for Role {
}
#[derive(Deserialize, Debug)]
-struct OpenAIResponseStreamEvent {
+pub struct OpenAIResponseStreamEvent {
pub id: Option,
pub object: String,
pub created: u32,
@@ -160,14 +165,14 @@ struct OpenAIResponseStreamEvent {
}
#[derive(Deserialize, Debug)]
-struct Usage {
+pub struct Usage {
pub prompt_tokens: u32,
pub completion_tokens: u32,
pub total_tokens: u32,
}
#[derive(Deserialize, Debug)]
-struct ChatChoiceDelta {
+pub struct ChatChoiceDelta {
pub index: u32,
pub delta: ResponseMessage,
pub finish_reason: Option,
@@ -191,3 +196,97 @@ struct OpenAIChoice {
pub fn init(cx: &mut AppContext) {
assistant::init(cx);
}
+
+pub async fn stream_completion(
+ api_key: String,
+ executor: Arc,
+ mut request: OpenAIRequest,
+) -> Result>> {
+ request.stream = true;
+
+ let (tx, rx) = futures::channel::mpsc::unbounded::>();
+
+ let json_data = serde_json::to_string(&request)?;
+ let mut response = Request::post(format!("{OPENAI_API_URL}/chat/completions"))
+ .header("Content-Type", "application/json")
+ .header("Authorization", format!("Bearer {}", api_key))
+ .body(json_data)?
+ .send_async()
+ .await?;
+
+ let status = response.status();
+ if status == StatusCode::OK {
+ executor
+ .spawn(async move {
+ let mut lines = BufReader::new(response.body_mut()).lines();
+
+ fn parse_line(
+ line: Result,
+ ) -> Result