compactの実装
This commit is contained in:
@@ -54,6 +54,12 @@ pub trait LlmClient: Send + Sync {
|
||||
request: Request,
|
||||
) -> Result<Pin<Box<dyn Stream<Item = Result<Event, ClientError>> + Send>>, ClientError>;
|
||||
|
||||
/// Clone this client into a new `Box<dyn LlmClient>`.
|
||||
///
|
||||
/// Used when a second client instance is needed (e.g. for context
|
||||
/// compaction) without access to the original construction parameters.
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient>;
|
||||
|
||||
/// 設定をバリデーションし、未サポートの設定があれば警告を返す
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -80,6 +86,10 @@ impl LlmClient for Box<dyn LlmClient> {
|
||||
(**self).stream(request).await
|
||||
}
|
||||
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
(**self).clone_boxed()
|
||||
}
|
||||
|
||||
fn validate_config(&self, config: &RequestConfig) -> Vec<ConfigWarning> {
|
||||
(**self).validate_config(config)
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use futures::{Stream, StreamExt, TryStreamExt, future::ready};
|
||||
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
||||
|
||||
/// Anthropic クライアント
|
||||
#[derive(Clone)]
|
||||
pub struct AnthropicClient {
|
||||
/// HTTPクライアント
|
||||
http_client: reqwest::Client,
|
||||
@@ -86,6 +87,10 @@ impl AnthropicClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for AnthropicClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
request: Request,
|
||||
|
||||
@@ -13,6 +13,7 @@ use futures::{Stream, StreamExt, TryStreamExt};
|
||||
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
||||
|
||||
/// Gemini クライアント
|
||||
#[derive(Clone)]
|
||||
pub struct GeminiClient {
|
||||
/// HTTPクライアント
|
||||
http_client: reqwest::Client,
|
||||
@@ -68,6 +69,10 @@ impl GeminiClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for GeminiClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
request: Request,
|
||||
|
||||
@@ -16,6 +16,7 @@ use futures::Stream;
|
||||
///
|
||||
/// 内部的にOpenAIClientを使用するラッパー、もしくはOpenAIClientと同様の実装を持つ。
|
||||
/// ここではOpenAIClient構成をカスタマイズして提供する。
|
||||
#[derive(Clone)]
|
||||
pub struct OllamaClient {
|
||||
inner: OpenAIClient,
|
||||
}
|
||||
@@ -53,6 +54,10 @@ impl OllamaClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for OllamaClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
request: Request,
|
||||
|
||||
@@ -14,6 +14,7 @@ use futures::{Stream, StreamExt, TryStreamExt};
|
||||
use reqwest::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
|
||||
|
||||
/// OpenAI クライアント
|
||||
#[derive(Clone)]
|
||||
pub struct OpenAIClient {
|
||||
/// HTTPクライアント
|
||||
http_client: reqwest::Client,
|
||||
@@ -85,6 +86,10 @@ impl OpenAIClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for OpenAIClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
request: Request,
|
||||
|
||||
@@ -118,9 +118,8 @@ impl AnthropicScheme {
|
||||
);
|
||||
|
||||
let anthropic_role = match role {
|
||||
Role::User => "user",
|
||||
Role::User | Role::System => "user",
|
||||
Role::Assistant => "assistant",
|
||||
Role::System => continue, // Skip system role items
|
||||
};
|
||||
|
||||
let parts: Vec<AnthropicContentPart> = content
|
||||
|
||||
@@ -216,9 +216,8 @@ impl GeminiScheme {
|
||||
);
|
||||
|
||||
let gemini_role = match role {
|
||||
Role::User => "user",
|
||||
Role::User | Role::System => "user",
|
||||
Role::Assistant => "model",
|
||||
Role::System => continue, // Skip system role items
|
||||
};
|
||||
|
||||
let parts: Vec<GeminiPart> = content
|
||||
|
||||
@@ -99,6 +99,20 @@ impl Item {
|
||||
// Message constructors
|
||||
// ========================================================================
|
||||
|
||||
/// Create a system message item with text content.
|
||||
///
|
||||
/// System items in history are sent as `role: "system"` on OpenAI,
|
||||
/// and as `role: "user"` on Anthropic/Gemini (which lack a system
|
||||
/// role in conversation items).
|
||||
pub fn system_message(text: impl Into<String>) -> Self {
|
||||
Self::Message {
|
||||
id: None,
|
||||
role: Role::System,
|
||||
content: vec![ContentPart::Text { text: text.into() }],
|
||||
status: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a user message item with text content
|
||||
pub fn user_message(text: impl Into<String>) -> Self {
|
||||
Self::Message {
|
||||
|
||||
@@ -302,6 +302,11 @@ impl<C: LlmClient, S: WorkerState> Worker<C, S> {
|
||||
&mut self.timeline
|
||||
}
|
||||
|
||||
/// Get a reference to the LLM client.
|
||||
pub fn client(&self) -> &C {
|
||||
&self.client
|
||||
}
|
||||
|
||||
/// Get a reference to the history
|
||||
pub fn history(&self) -> &[Item] {
|
||||
&self.history
|
||||
|
||||
@@ -45,6 +45,10 @@ impl MockLlmClient {
|
||||
|
||||
#[async_trait]
|
||||
impl LlmClient for MockLlmClient {
|
||||
fn clone_boxed(&self) -> Box<dyn LlmClient> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
async fn stream(
|
||||
&self,
|
||||
_request: Request,
|
||||
|
||||
Reference in New Issue
Block a user