Compare commits
5 Commits
thread3
...
language-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
500b581a46 | ||
|
|
ccc6e13f99 | ||
|
|
aceb5581b3 | ||
|
|
24c8bad8de | ||
|
|
8f6ea25a95 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -28,3 +28,4 @@ DerivedData/
|
||||
.vscode
|
||||
.wrangler
|
||||
.flatpak-builder
|
||||
.aider*
|
||||
|
||||
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -5832,6 +5832,20 @@ dependencies = [
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language_model"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"client",
|
||||
"collections",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"proto",
|
||||
"schemars",
|
||||
"util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language_selector"
|
||||
version = "0.1.0"
|
||||
@@ -6427,6 +6441,18 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "miner"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"project",
|
||||
"schemars",
|
||||
"worktree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
|
||||
@@ -53,11 +53,13 @@ members = [
|
||||
"crates/languages",
|
||||
"crates/live_kit_client",
|
||||
"crates/live_kit_server",
|
||||
"crates/language_model",
|
||||
"crates/lsp",
|
||||
"crates/markdown",
|
||||
"crates/markdown_preview",
|
||||
"crates/media",
|
||||
"crates/menu",
|
||||
"crates/miner",
|
||||
"crates/multi_buffer",
|
||||
"crates/node_runtime",
|
||||
"crates/notifications",
|
||||
@@ -200,6 +202,7 @@ image_viewer = { path = "crates/image_viewer" }
|
||||
inline_completion_button = { path = "crates/inline_completion_button" }
|
||||
journal = { path = "crates/journal" }
|
||||
language = { path = "crates/language" }
|
||||
language_model = { path = "crates/language_model" }
|
||||
language_selector = { path = "crates/language_selector" }
|
||||
language_tools = { path = "crates/language_tools" }
|
||||
languages = { path = "crates/languages" }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{AppContext, BorrowAppContext};
|
||||
use crate::{AppContext, BorrowAppContext, Model};
|
||||
|
||||
/// A marker trait for types that can be stored in GPUI's global state.
|
||||
///
|
||||
@@ -72,3 +72,5 @@ impl<T: Global> UpdateGlobal for T {
|
||||
cx.set_global(global)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Global> Global for Model<T> {}
|
||||
|
||||
20
crates/language_model/Cargo.toml
Normal file
20
crates/language_model/Cargo.toml
Normal file
@@ -0,0 +1,20 @@
|
||||
[package]
|
||||
name = "language_model"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/language_model.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
client.workspace = true
|
||||
collections.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
proto.workspace = true
|
||||
schemars.workspace = true
|
||||
util.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
94
crates/language_model/src/language_model.rs
Normal file
94
crates/language_model/src/language_model.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
mod registry;
|
||||
mod zed_cloud;
|
||||
|
||||
pub use registry::*;
|
||||
pub use zed_cloud::*;
|
||||
|
||||
use anyhow::Result;
|
||||
use futures::{future::BoxFuture, stream::BoxStream};
|
||||
use gpui::{AnyView, AppContext, SharedString, Task, WindowContext};
|
||||
use schemars::schema::RootSchema;
|
||||
|
||||
pub trait LanguageModel {
|
||||
fn is_authenticated(&self, cx: &mut AppContext) -> bool;
|
||||
|
||||
fn authenticate(&self, cx: &mut AppContext) -> Task<Result<()>>;
|
||||
|
||||
fn authentication_prompt(&self, cx: &mut WindowContext) -> AnyView;
|
||||
|
||||
fn reset_credentials(&self, cx: &mut AppContext) -> Task<Result<()>>;
|
||||
|
||||
fn count_tokens(
|
||||
&self,
|
||||
request: LanguageModelRequest,
|
||||
cx: &mut AppContext,
|
||||
) -> BoxFuture<'static, Result<usize>>;
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
request: LanguageModelRequest,
|
||||
cx: &mut AppContext,
|
||||
) -> BoxFuture<'static, Result<BoxStream<'static, Result<LanguageModelOutput>>>>;
|
||||
}
|
||||
|
||||
pub enum LanguageModelOutput {
|
||||
Text(String),
|
||||
ToolCall(ToolCall),
|
||||
}
|
||||
|
||||
pub struct ToolCall {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub arguments: String,
|
||||
}
|
||||
|
||||
pub struct LanguageModelRequest {
|
||||
pub tools: Vec<LanguageModelTool>,
|
||||
pub messages: Vec<LanguageModelRequestMessage>,
|
||||
pub stop: Vec<String>,
|
||||
pub temperature: f32,
|
||||
}
|
||||
|
||||
pub struct LanguageModelTool {
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub parameters: RootSchema,
|
||||
}
|
||||
|
||||
pub struct LanguageModelRequestMessage {
|
||||
role: Role,
|
||||
content: String,
|
||||
}
|
||||
|
||||
pub enum Role {
|
||||
User,
|
||||
Assistant,
|
||||
System,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct LanguageModelId(pub SharedString);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct LanguageModelName(pub SharedString);
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct LanguageModelProviderName(SharedString);
|
||||
|
||||
impl From<String> for LanguageModelId {
|
||||
fn from(value: String) -> Self {
|
||||
Self(SharedString::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for LanguageModelName {
|
||||
fn from(value: String) -> Self {
|
||||
Self(SharedString::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for LanguageModelProviderName {
|
||||
fn from(value: String) -> Self {
|
||||
Self(SharedString::from(value))
|
||||
}
|
||||
}
|
||||
102
crates/language_model/src/registry.rs
Normal file
102
crates/language_model/src/registry.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::{LanguageModel, LanguageModelId, LanguageModelName, LanguageModelProviderName};
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use gpui::{AppContext, Global, Model, ModelContext};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProvidedLanguageModel {
|
||||
pub id: LanguageModelId,
|
||||
pub name: LanguageModelName,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct AvailableLanguageModel {
|
||||
pub provider: LanguageModelProviderName,
|
||||
pub model: ProvidedLanguageModel,
|
||||
}
|
||||
|
||||
pub trait LanguageModelProvider: 'static {
|
||||
fn name(&self, cx: &AppContext) -> LanguageModelProviderName;
|
||||
|
||||
fn provided_models(&self, cx: &AppContext) -> Vec<ProvidedLanguageModel>;
|
||||
|
||||
fn model(&self, id: LanguageModelId, cx: &AppContext) -> Result<Arc<dyn LanguageModel>>;
|
||||
}
|
||||
|
||||
impl<T: LanguageModelProvider> LanguageModelProvider for Model<T> {
|
||||
fn name(&self, cx: &AppContext) -> LanguageModelProviderName {
|
||||
self.read(cx).name(cx)
|
||||
}
|
||||
|
||||
fn provided_models(&self, cx: &AppContext) -> Vec<ProvidedLanguageModel> {
|
||||
self.read(cx).provided_models(cx)
|
||||
}
|
||||
|
||||
fn model(&self, id: LanguageModelId, cx: &AppContext) -> Result<Arc<dyn LanguageModel>> {
|
||||
self.read(cx).model(id, cx)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LanguageModelRegistry {
|
||||
providers: HashMap<LanguageModelProviderName, Box<dyn LanguageModelProvider>>,
|
||||
}
|
||||
|
||||
impl Global for LanguageModelRegistry {}
|
||||
|
||||
impl LanguageModelRegistry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
providers: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register<T>(&mut self, provider: Model<T>, cx: &mut ModelContext<Self>)
|
||||
where
|
||||
T: LanguageModelProvider,
|
||||
{
|
||||
cx.observe(&provider, |_, _, cx| {
|
||||
cx.notify();
|
||||
})
|
||||
.detach();
|
||||
|
||||
if self
|
||||
.providers
|
||||
.insert(provider.name(cx), Box::new(provider.clone()))
|
||||
.is_some()
|
||||
{
|
||||
panic!(
|
||||
"A provider with the name {} already exists",
|
||||
provider.name(cx).0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn available_models(&self, cx: &AppContext) -> Vec<AvailableLanguageModel> {
|
||||
self.providers
|
||||
.values()
|
||||
.flat_map(|provider| {
|
||||
provider
|
||||
.provided_models(cx)
|
||||
.into_iter()
|
||||
.map(|model| AvailableLanguageModel {
|
||||
provider: provider.name(cx),
|
||||
model,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn model(
|
||||
&mut self,
|
||||
info: &AvailableLanguageModel,
|
||||
cx: &mut AppContext,
|
||||
) -> Result<Arc<dyn LanguageModel>> {
|
||||
let provider = self
|
||||
.providers
|
||||
.get(&info.provider)
|
||||
.ok_or_else(|| anyhow::anyhow!("No provider found for name: {:?}", info.provider))?;
|
||||
|
||||
provider.model(info.model.id.clone(), cx)
|
||||
}
|
||||
}
|
||||
108
crates/language_model/src/zed_cloud.rs
Normal file
108
crates/language_model/src/zed_cloud.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
use client::Client;
|
||||
use gpui::ModelContext;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
LanguageModel, LanguageModelId, LanguageModelProvider, LanguageModelProviderName,
|
||||
ProvidedLanguageModel,
|
||||
};
|
||||
|
||||
pub struct ZedCloudModelProvider {
|
||||
client: Arc<Client>,
|
||||
available_models: Vec<ProvidedLanguageModel>,
|
||||
}
|
||||
|
||||
impl ZedCloudModelProvider {
|
||||
pub fn new(client: Arc<Client>, cx: &mut ModelContext<Self>) -> Self {
|
||||
let authenticated = client.user_id().is_some();
|
||||
if authenticated {
|
||||
client.request(client::proto::GetAvail {});
|
||||
|
||||
let models = cx.spawn(|this, mut cx| async move {
|
||||
let response = this.update(&mut cx, |this, _| {})?;
|
||||
response.await
|
||||
});
|
||||
|
||||
match models.await {
|
||||
Ok(models) => {
|
||||
self.available_models = models
|
||||
.models
|
||||
.into_iter()
|
||||
.map(|model| ProvidedLanguageModel {
|
||||
id: LanguageModelId(model.id),
|
||||
name: model.name,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to fetch language models: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
client,
|
||||
available_models: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LanguageModelProvider for ZedCloudModelProvider {
|
||||
fn name(&self, _cx: &gpui::AppContext) -> crate::LanguageModelProviderName {
|
||||
LanguageModelProviderName("Zed Cloud".into())
|
||||
}
|
||||
|
||||
fn provided_models(&self, _cx: &gpui::AppContext) -> Vec<ProvidedLanguageModel> {
|
||||
self.available_models.clone()
|
||||
}
|
||||
|
||||
fn model(
|
||||
&self,
|
||||
id: LanguageModelId,
|
||||
_cx: &gpui::AppContext,
|
||||
) -> gpui::Result<Arc<dyn LanguageModel>> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct ZedCloudModel {
|
||||
id: LanguageModelId,
|
||||
client: Arc<Client>,
|
||||
}
|
||||
|
||||
impl LanguageModel for ZedCloudModel {
|
||||
fn is_authenticated(&self, cx: &mut gpui::AppContext) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn authenticate(&self, cx: &mut gpui::AppContext) -> gpui::Task<gpui::Result<()>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn authentication_prompt(&self, cx: &mut gpui::WindowContext) -> gpui::AnyView {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn reset_credentials(&self, cx: &mut gpui::AppContext) -> gpui::Task<gpui::Result<()>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn count_tokens(
|
||||
&self,
|
||||
request: crate::LanguageModelRequest,
|
||||
cx: &mut gpui::AppContext,
|
||||
) -> futures::future::BoxFuture<'static, gpui::Result<usize>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn complete(
|
||||
&self,
|
||||
request: crate::LanguageModelRequest,
|
||||
cx: &mut gpui::AppContext,
|
||||
) -> futures::future::BoxFuture<
|
||||
'static,
|
||||
gpui::Result<futures::stream::BoxStream<'static, gpui::Result<crate::LanguageModelOutput>>>,
|
||||
> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
18
crates/miner/Cargo.toml
Normal file
18
crates/miner/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "miner"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lib]
|
||||
path = "src/miner.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
gpui.workspace = true
|
||||
project.workspace = true
|
||||
schemars.workspace = true
|
||||
worktree.workspace = true
|
||||
1
crates/miner/src/language_model.rs
Normal file
1
crates/miner/src/language_model.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
24
crates/miner/src/miner.rs
Normal file
24
crates/miner/src/miner.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
mod language_model;
|
||||
|
||||
use futures::Stream;
|
||||
use gpui::Model;
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct Miner {
|
||||
project: Model<Project>,
|
||||
language_model: Arc<dyn LanguageModel>,
|
||||
}
|
||||
|
||||
impl Miner {
|
||||
pub fn new(project: Model<Project>, language_model: Arc<dyn LanguageModel>) -> Self {
|
||||
Self {
|
||||
project,
|
||||
language_model,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LanguageModel: Send + Sync {
|
||||
fn generate(&self) -> Box<dyn Stream<Item = String> + Send + Unpin>;
|
||||
}
|
||||
@@ -201,6 +201,8 @@ message Envelope {
|
||||
|
||||
JoinHostedProject join_hosted_project = 164;
|
||||
|
||||
GetAvailableLanguageModels get_available_language_models = 211;
|
||||
GetAvailableLanguageModelsResponse get_available_language_models_response = 212; // current max
|
||||
CompleteWithLanguageModel complete_with_language_model = 166;
|
||||
LanguageModelResponse language_model_response = 167;
|
||||
CountTokensWithLanguageModel count_tokens_with_language_model = 168;
|
||||
@@ -255,7 +257,7 @@ message Envelope {
|
||||
TaskTemplates task_templates = 206;
|
||||
|
||||
LinkedEditingRange linked_editing_range = 209;
|
||||
LinkedEditingRangeResponse linked_editing_range_response = 210; // current max
|
||||
LinkedEditingRangeResponse linked_editing_range_response = 210;
|
||||
}
|
||||
|
||||
reserved 158 to 161;
|
||||
@@ -1959,6 +1961,19 @@ message SetRoomParticipantRole {
|
||||
ChannelRole role = 3;
|
||||
}
|
||||
|
||||
message GetAvailableLanguageModels {}
|
||||
|
||||
message GetAvailableLanguageModelsResponse {
|
||||
repeated LanguageModelInfo models = 1;
|
||||
}
|
||||
|
||||
message LanguageModelInfo {
|
||||
string id = 1; // Unique identifier for the model
|
||||
string name = 2; // Human-readable name of the model
|
||||
string description = 3; // Brief description of the model's capabilities
|
||||
uint32 max_tokens = 4; // Maximum number of tokens the model can process
|
||||
}
|
||||
|
||||
message CompleteWithLanguageModel {
|
||||
string model = 1;
|
||||
repeated LanguageModelRequestMessage messages = 2;
|
||||
|
||||
@@ -179,6 +179,7 @@ messages!(
|
||||
(FormatBuffers, Foreground),
|
||||
(FormatBuffersResponse, Foreground),
|
||||
(FuzzySearchUsers, Foreground),
|
||||
(GetAvailableLanguageModels, Background),
|
||||
(GetCachedEmbeddings, Background),
|
||||
(GetCachedEmbeddingsResponse, Background),
|
||||
(GetChannelMembers, Foreground),
|
||||
|
||||
Reference in New Issue
Block a user