compactの実装

This commit is contained in:
2026-04-12 07:09:48 +09:00
parent 47c59a416e
commit bcc7faa0ba
19 changed files with 439 additions and 21 deletions
@@ -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
+14
View File
@@ -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 {
+5
View File
@@ -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
+4
View File
@@ -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,