docs: generalize llm-engine comments
This commit is contained in:
parent
34856b9900
commit
1505c5e8a3
|
|
@ -20,7 +20,7 @@ Does not own:
|
||||||
|
|
||||||
## Design notes
|
## Design notes
|
||||||
|
|
||||||
Macros reduce boilerplate, but they must not imply capability. A generated tool definition is still subject to manifest permissions, Worker scope, and runtime policy.
|
Macros reduce boilerplate, but they must not imply capability. A generated tool definition is still subject to host permissions, application scope, and runtime policy.
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,10 @@ Owns:
|
||||||
|
|
||||||
Does not own:
|
Does not own:
|
||||||
|
|
||||||
- Worker names, sockets, process lifecycle, or scope delegation (`worker`)
|
- Host application names, sockets, process lifecycle, or scope delegation
|
||||||
- product CLI shape (`yoi`)
|
- Product CLI shape
|
||||||
- provider catalog and secret resolution (`provider`, `secrets`)
|
- Provider catalog and secret resolution
|
||||||
- durable Worker current state (`session-store` worker metadata)
|
- Durable application state outside engine history
|
||||||
|
|
||||||
## Design notes
|
## Design notes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,9 +213,9 @@ pub struct Engine<C: LlmClient, S: EngineState = Mutable> {
|
||||||
/// stream events become visible.
|
/// stream events become visible.
|
||||||
lifecycle_trace_cbs: Vec<Arc<dyn Fn(usize, usize, &str, &Value) + Send + Sync>>,
|
lifecycle_trace_cbs: Vec<Arc<dyn Fn(usize, usize, &str, &Value) + Send + Sync>>,
|
||||||
/// Non-fatal warning callbacks. Invoked when the Engine wants to
|
/// Non-fatal warning callbacks. Invoked when the Engine wants to
|
||||||
/// surface an advisory message to the upper layer (e.g. Worker) so it
|
/// surface an advisory message to the caller so it can be forwarded
|
||||||
/// can be forwarded to the user — distinct from `tracing::warn!`,
|
/// to the user — distinct from `tracing::warn!`, which is for
|
||||||
/// which is for developer-facing logs.
|
/// developer-facing logs.
|
||||||
warning_cbs: Vec<Box<dyn Fn(&str) + Send + Sync>>,
|
warning_cbs: Vec<Box<dyn Fn(&str) + Send + Sync>>,
|
||||||
/// Tool-result callbacks. Invoked once per completed tool call
|
/// Tool-result callbacks. Invoked once per completed tool call
|
||||||
/// after post-execution interceptors and the output byte-cap
|
/// after post-execution interceptors and the output byte-cap
|
||||||
|
|
@ -253,8 +253,8 @@ pub struct Engine<C: LlmClient, S: EngineState = Mutable> {
|
||||||
/// Plumbed into [`Request::cache_anchor`] at request build time.
|
/// Plumbed into [`Request::cache_anchor`] at request build time.
|
||||||
cache_anchor: Option<usize>,
|
cache_anchor: Option<usize>,
|
||||||
/// Conversation-scoped cache key, set by higher layers. Plumbed into
|
/// Conversation-scoped cache key, set by higher layers. Plumbed into
|
||||||
/// [`Request::cache_key`] at request build time. Worker 側では
|
/// [`Request::cache_key`] at request build time. Callers should pass a
|
||||||
/// `SegmentId` を渡す。
|
/// stable conversation identifier when the backend benefits from one.
|
||||||
cache_key: Option<String>,
|
cache_key: Option<String>,
|
||||||
/// State marker
|
/// State marker
|
||||||
_state: PhantomData<S>,
|
_state: PhantomData<S>,
|
||||||
|
|
@ -486,8 +486,8 @@ impl<C: LlmClient, S: EngineState> Engine<C, S> {
|
||||||
///
|
///
|
||||||
/// Fired after `post_tool_call` interceptors and any `content`
|
/// Fired after `post_tool_call` interceptors and any `content`
|
||||||
/// truncation from `tool_output_limits`, so the callback observes
|
/// truncation from `tool_output_limits`, so the callback observes
|
||||||
/// exactly what is persisted to history. Intended for upper layers
|
/// exactly what is persisted to history. Intended for callers that need
|
||||||
/// (e.g. Worker) to forward tool results to clients.
|
/// to forward tool results to clients.
|
||||||
pub fn on_tool_result(&mut self, callback: impl Fn(&ToolResult) + Send + Sync + 'static) {
|
pub fn on_tool_result(&mut self, callback: impl Fn(&ToolResult) + Send + Sync + 'static) {
|
||||||
self.tool_result_cbs.push(Box::new(callback));
|
self.tool_result_cbs.push(Box::new(callback));
|
||||||
}
|
}
|
||||||
|
|
@ -1121,10 +1121,10 @@ impl<C: LlmClient, S: EngineState> Engine<C, S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drain interceptor-side inputs that are meant to land in
|
// Drain interceptor-side inputs that are meant to land in
|
||||||
// history (notifications, cross-Worker events, system
|
// history (notifications, external events, system reminders).
|
||||||
// reminders). These are committed *before* the per-request
|
// These are committed *before* the per-request clone so they
|
||||||
// clone so they participate in the LLM request below and
|
// participate in the LLM request below and get persisted by
|
||||||
// get persisted by the upper layer that owns history.json.
|
// the caller that owns durable history.
|
||||||
let pending = self.interceptor.pending_history_appends().await;
|
let pending = self.interceptor.pending_history_appends().await;
|
||||||
if !pending.is_empty() {
|
if !pending.is_empty() {
|
||||||
self.append_history_items(pending);
|
self.append_history_items(pending);
|
||||||
|
|
@ -1301,9 +1301,7 @@ impl<C: LlmClient, S: EngineState> Engine<C, S> {
|
||||||
self.turn_count += 1;
|
self.turn_count += 1;
|
||||||
|
|
||||||
// Collect and commit assistant items. Routed through
|
// Collect and commit assistant items. Routed through
|
||||||
// `append_history_items` so observers (e.g. the
|
// `append_history_items` so observers see each item as it lands.
|
||||||
// Worker-side per-item session-log committer) see each item
|
|
||||||
// as it lands.
|
|
||||||
let reasoning_items = self.thinking_block_collector.take_collected();
|
let reasoning_items = self.thinking_block_collector.take_collected();
|
||||||
let text_blocks = self.text_block_collector.take_collected();
|
let text_blocks = self.text_block_collector.take_collected();
|
||||||
let tool_calls = self.tool_call_collector.take_collected();
|
let tool_calls = self.tool_call_collector.take_collected();
|
||||||
|
|
@ -1603,8 +1601,7 @@ impl<C: LlmClient, S: EngineState> Engine<C, S> {
|
||||||
}
|
}
|
||||||
Ok(ToolExecutionResult::Completed(results)) => {
|
Ok(ToolExecutionResult::Completed(results)) => {
|
||||||
// Route per-result pushes through the callback path so
|
// Route per-result pushes through the callback path so
|
||||||
// observers (e.g. the Worker-side per-item session-log
|
// observers see each tool result as it lands.
|
||||||
// committer) see each tool result as it lands.
|
|
||||||
let items = results.into_iter().map(|result| {
|
let items = results.into_iter().map(|result| {
|
||||||
Item::tool_result_item(
|
Item::tool_result_item(
|
||||||
&result.tool_use_id,
|
&result.tool_use_id,
|
||||||
|
|
@ -1707,9 +1704,9 @@ impl<C: LlmClient> Engine<C, Mutable> {
|
||||||
|
|
||||||
/// Install byte-size caps for tool execution `content`.
|
/// Install byte-size caps for tool execution `content`.
|
||||||
///
|
///
|
||||||
/// Passing `None` (the default) disables truncation. Higher layers
|
/// Passing `None` (the default) disables truncation. Callers translate
|
||||||
/// (e.g. Worker) translate manifest configuration into a concrete
|
/// their own configuration into a concrete [`ToolOutputLimits`] and
|
||||||
/// [`ToolOutputLimits`] and install it here.
|
/// install it here.
|
||||||
pub fn set_tool_output_limits(&mut self, limits: Option<ToolOutputLimits>) {
|
pub fn set_tool_output_limits(&mut self, limits: Option<ToolOutputLimits>) {
|
||||||
self.tool_output_limits = limits;
|
self.tool_output_limits = limits;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
//! Interceptor - control flow delegation for the Engine execution loop
|
//! Interceptor - control flow delegation for the Engine execution loop
|
||||||
//!
|
//!
|
||||||
//! Defines the [`Interceptor`] trait that upper layers (e.g. Worker) implement
|
//! Defines the [`Interceptor`] trait that callers implement to inject
|
||||||
//! to inject orchestration decisions (approval, skip, pause, abort)
|
//! orchestration decisions (approval, skip, pause, abort) into the Engine's
|
||||||
//! into the Engine's turn loop without the Engine knowing about
|
//! turn loop without the Engine knowing about host-application concepts.
|
||||||
//! higher-level concepts.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
@ -132,8 +131,8 @@ pub struct ToolResultInfo {
|
||||||
/// Intercepts the Engine execution loop at key decision points.
|
/// Intercepts the Engine execution loop at key decision points.
|
||||||
///
|
///
|
||||||
/// All methods have default implementations that let the Engine
|
/// All methods have default implementations that let the Engine
|
||||||
/// proceed without intervention. Upper layers (e.g. Worker) provide
|
/// proceed without intervention. Callers provide richer implementations for
|
||||||
/// richer implementations for approval flows, permission checks, etc.
|
/// approval flows, permission checks, etc.
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Interceptor: Send + Sync {
|
pub trait Interceptor: Send + Sync {
|
||||||
/// Called after receiving user input, before adding to history.
|
/// Called after receiving user input, before adding to history.
|
||||||
|
|
@ -149,7 +148,7 @@ pub trait Interceptor: Send + Sync {
|
||||||
///
|
///
|
||||||
/// Use this for inputs that arrive from outside the LLM and need
|
/// Use this for inputs that arrive from outside the LLM and need
|
||||||
/// to be reflected in the on-disk history — notifications,
|
/// to be reflected in the on-disk history — notifications,
|
||||||
/// cross-Worker events, system reminders. Do **not** use
|
/// external events, system reminders. Do **not** use
|
||||||
/// [`Self::pre_llm_request`] for that purpose: it mutates a
|
/// [`Self::pre_llm_request`] for that purpose: it mutates a
|
||||||
/// per-request clone, so any committed assistant response that
|
/// per-request clone, so any committed assistant response that
|
||||||
/// reacts to the injection would have no visible trigger on the
|
/// reacts to the injection would have no visible trigger on the
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,11 @@
|
||||||
//! `Scheme` 実装と通信層が要求する認証要件、および動的認証プロバイダ。
|
//! `Scheme` 実装と通信層が要求する認証要件、および動的認証プロバイダ。
|
||||||
//!
|
//!
|
||||||
//! マニフェスト側の型(`ModelConfig` / `SchemeKind` / `AuthRef`)は
|
|
||||||
//! `crates/manifest` に置き、llm-engine はそれを知らずに済む。
|
|
||||||
//! `AuthRequirement` は scheme が宣言する「この scheme はどんな認証を
|
//! `AuthRequirement` は scheme が宣言する「この scheme はどんな認証を
|
||||||
//! 期待するか」のランタイム記述で、manifest 側の `AuthRef` との
|
//! 期待するか」のランタイム記述で、設定ファイルや環境変数などから
|
||||||
//! 照合(`AuthRef → ResolvedAuth` 変換の適否)は `crates/provider`
|
//! [`super::transport::ResolvedAuth`] を組み立てる責務は呼び出し側にある。
|
||||||
//! で行う。
|
|
||||||
//!
|
//!
|
||||||
//! Codex OAuth のようにリクエスト毎にトークンが変わり得る認証は
|
//! リクエスト毎にトークンが変わり得る認証は [`AuthProvider`] trait を
|
||||||
//! [`AuthProvider`] trait を `crates/provider` 側で実装し、
|
//! 実装し、[`super::transport::ResolvedAuth::Custom`] 経由で transport に渡す。
|
||||||
//! [`super::transport::ResolvedAuth::Custom`] 経由で transport に渡す。
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use reqwest::header::{HeaderName, HeaderValue};
|
use reqwest::header::{HeaderName, HeaderValue};
|
||||||
|
|
@ -27,16 +23,15 @@ pub enum AuthRequirement {
|
||||||
XApiKey,
|
XApiKey,
|
||||||
/// クエリパラメータ `?<name>=<token>`(Gemini 形式)
|
/// クエリパラメータ `?<name>=<token>`(Gemini 形式)
|
||||||
QueryParam { name: &'static str },
|
QueryParam { name: &'static str },
|
||||||
/// 複合ヘッダ(Codex OAuth 等、`crates/provider` 側で解決)
|
/// 複合ヘッダ(呼び出し側が [`AuthProvider`] で解決)
|
||||||
Custom,
|
Custom,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// リクエスト毎に認証ヘッダを動的に組み立てるプロバイダ。
|
/// リクエスト毎に認証ヘッダを動的に組み立てるプロバイダ。
|
||||||
///
|
///
|
||||||
/// Codex OAuth のように access_token が refresh で更新されたり、
|
/// access token が refresh で更新されたり、複数ヘッダを同時に注入する
|
||||||
/// `ChatGPT-Account-Id` / `X-OpenAI-Fedramp` のような複数ヘッダを
|
/// 必要があるケースで使う。実体は呼び出し側に置き、llm-engine は
|
||||||
/// 同時に注入する必要があるケースで使う。実体は `crates/provider`
|
/// trait を知るだけ。
|
||||||
/// 側に置き、llm-engine は trait を知るだけ。
|
|
||||||
///
|
///
|
||||||
/// 返したヘッダはそのまま `HeaderMap` に挿入される。`Authorization`
|
/// 返したヘッダはそのまま `HeaderMap` に挿入される。`Authorization`
|
||||||
/// 含む scheme 既定の認証ヘッダは送出されないので、必要なら
|
/// 含む scheme 既定の認証ヘッダは送出されないので、必要なら
|
||||||
|
|
@ -46,11 +41,10 @@ pub trait AuthProvider: Send + Sync + std::fmt::Debug {
|
||||||
/// 1 リクエスト分の認証ヘッダを返す。refresh が必要なら内部で行う。
|
/// 1 リクエスト分の認証ヘッダを返す。refresh が必要なら内部で行う。
|
||||||
async fn headers(&self) -> Result<Vec<(HeaderName, HeaderValue)>, ClientError>;
|
async fn headers(&self) -> Result<Vec<(HeaderName, HeaderValue)>, ClientError>;
|
||||||
|
|
||||||
/// ChatGPT Codex backend 向けの複合認証かどうか。
|
/// Conversation header / request compression が必要な backend profile かどうか。
|
||||||
///
|
///
|
||||||
/// transport は provider crate の具象型を知らないため、この hook だけで
|
/// transport は呼び出し側の具象型を知らないため、この hook だけで
|
||||||
/// Codex CLI 互換の wire behavior(conversation header / request compression 等)
|
/// 追加の wire behavior を切り替える。
|
||||||
/// を切り替える。
|
|
||||||
fn is_codex_backend(&self) -> bool {
|
fn is_codex_backend(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,8 +45,8 @@ pub trait Scheme: Clone + Send + Sync + 'static {
|
||||||
/// プロバイダもあるため、モデル ID を受け取る。
|
/// プロバイダもあるため、モデル ID を受け取る。
|
||||||
fn path(&self, model_id: &str) -> String;
|
fn path(&self, model_id: &str) -> String;
|
||||||
|
|
||||||
/// この scheme が要求する認証形式。`build_client` 時に
|
/// この scheme が要求する認証形式。呼び出し側は client 構築時に
|
||||||
/// `manifest::AuthRef` と照合する。
|
/// 設定された認証情報と照合する。
|
||||||
fn required_auth(&self) -> AuthRequirement;
|
fn required_auth(&self) -> AuthRequirement;
|
||||||
|
|
||||||
/// `Content-Type` 以外の追加ヘッダ。`anthropic-version` / `anthropic-beta` 等。
|
/// `Content-Type` 以外の追加ヘッダ。`anthropic-version` / `anthropic-beta` 等。
|
||||||
|
|
@ -78,8 +78,7 @@ pub trait Scheme: Clone + Send + Sync + 'static {
|
||||||
|
|
||||||
/// scheme 既定の capability。モデル ID に関係なく、この wire で
|
/// scheme 既定の capability。モデル ID に関係なく、この wire で
|
||||||
/// 安全に送れる最小共通項を返す。既知モデル ID の能力テーブルは
|
/// 安全に送れる最小共通項を返す。既知モデル ID の能力テーブルは
|
||||||
/// `provider::capability::lookup` 側(高レベル構築層)の責務で、
|
/// 高レベルの client 構築層の責務で、scheme はここには関与しない。
|
||||||
/// scheme はここには関与しない。
|
|
||||||
fn default_capability(&self) -> ModelCapability;
|
fn default_capability(&self) -> ModelCapability;
|
||||||
|
|
||||||
/// scheme 側でサポートしていない `RequestConfig` フィールドを
|
/// scheme 側でサポートしていない `RequestConfig` フィールドを
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
//! OpenAI Responses scheme の wire-level 既定 capability。
|
//! OpenAI Responses scheme の wire-level 既定 capability。
|
||||||
//!
|
//!
|
||||||
//! モデル ID 固有のテーブル(`gpt-5` / `codex-` 系など)は高レベル構築層
|
//! モデル ID 固有の能力テーブルは高レベルの client 構築層の責務。
|
||||||
//! (`provider::capability`)の責務。ここでは wire の保守的 default のみ。
|
//! ここでは wire の保守的 default のみ。
|
||||||
|
|
||||||
use crate::llm_client::capability::{
|
use crate::llm_client::capability::{
|
||||||
CacheStrategy, ModelCapability, StructuredOutput, ToolCallingSupport,
|
CacheStrategy, ModelCapability, StructuredOutput, ToolCallingSupport,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! `response.*` 名前空間の SSE を共通の [`Event`](crate::llm_client::event::Event)
|
//! `response.*` 名前空間の SSE を共通の [`Event`](crate::llm_client::event::Event)
|
||||||
//! に変換する。Responses の (output_index, content_index) 2 次元座標と
|
//! に変換する。Responses の (output_index, content_index) 2 次元座標と
|
||||||
//! yoi 側 1 次元 `BlockStart/Delta/Stop::index` のマッピングは
|
//! この crate の 1 次元 `BlockStart/Delta/Stop::index` のマッピングは
|
||||||
//! [`OpenAIResponsesState`] が保持する。
|
//! [`OpenAIResponsesState`] が保持する。
|
||||||
|
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Chat Completions とは別物の item-based wire format。reasoning item と
|
//! Chat Completions とは別物の item-based wire format。reasoning item と
|
||||||
//! function_call item が first-class で、SSE イベントも `response.*` 名前空間で
|
//! function_call item が first-class で、SSE イベントも `response.*` 名前空間で
|
||||||
//! 流れる。ChatGPT OAuth 経路 (codex) は本 scheme 必須。
|
//! 流れる。
|
||||||
//!
|
//!
|
||||||
//! - リクエスト JSON 生成: [`request`]
|
//! - リクエスト JSON 生成: [`request`]
|
||||||
//! - SSE イベントパース → [`Event`](crate::llm_client::event::Event) 変換: [`events`]
|
//! - SSE イベントパース → [`Event`](crate::llm_client::event::Event) 変換: [`events`]
|
||||||
|
|
@ -19,10 +19,10 @@ pub use scheme_impl::OpenAIResponsesState;
|
||||||
/// `store` / `include_encrypted_content` / `send_max_output_tokens` /
|
/// `store` / `include_encrypted_content` / `send_max_output_tokens` /
|
||||||
/// `send_sampling_params` は scheme 固定の wire 設定で、デフォルトは
|
/// `send_sampling_params` は scheme 固定の wire 設定で、デフォルトは
|
||||||
/// 公式 OpenAI Responses API 向け (stateless + ZDR + `max_output_tokens`
|
/// 公式 OpenAI Responses API 向け (stateless + ZDR + `max_output_tokens`
|
||||||
/// / `temperature` / `top_p` 送出可)。ChatGPT backend (codex-oauth) の
|
/// / `temperature` / `top_p` 送出可)。受理パラメータが subset の
|
||||||
/// ように受理パラメータが subset の経路では provider 層で
|
/// 互換 backend では client 構築層で `send_max_output_tokens=false` /
|
||||||
/// `send_max_output_tokens=false` / `send_sampling_params=false` に
|
/// `send_sampling_params=false` に上書きする。`ModelCapability` には
|
||||||
/// 上書きする。`ModelCapability` には入れない(モデル能力ではなく wire policy)。
|
/// 入れない(モデル能力ではなく wire policy)。
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct OpenAIResponsesScheme {
|
pub struct OpenAIResponsesScheme {
|
||||||
/// サーバ側に response を保存するか。ZDR/stateless 運用では `false`。
|
/// サーバ側に response を保存するか。ZDR/stateless 運用では `false`。
|
||||||
|
|
@ -31,12 +31,12 @@ pub struct OpenAIResponsesScheme {
|
||||||
/// `store=false` で reasoning を使うなら必須。
|
/// `store=false` で reasoning を使うなら必須。
|
||||||
pub include_encrypted_content: bool,
|
pub include_encrypted_content: bool,
|
||||||
/// `max_output_tokens` を body に載せるか。公式 OpenAI Responses API は
|
/// `max_output_tokens` を body に載せるか。公式 OpenAI Responses API は
|
||||||
/// 受理するが、ChatGPT backend (codex-oauth) は `Unsupported parameter`
|
/// 受理するが、互換 backend によっては `Unsupported parameter` で
|
||||||
/// で 400 を返すため、その経路では `false` にする。
|
/// 400 を返すため、その経路では `false` にする。
|
||||||
pub send_max_output_tokens: bool,
|
pub send_max_output_tokens: bool,
|
||||||
/// `temperature` / `top_p` を body に載せるか。公式 OpenAI Responses API
|
/// `temperature` / `top_p` を body に載せるか。公式 OpenAI Responses API
|
||||||
/// は受理するが、ChatGPT backend (codex-oauth) は `Unsupported parameter`
|
/// は受理するが、互換 backend によっては `Unsupported parameter` で
|
||||||
/// で 400 を返すため、その経路では `false` にする。
|
/// 400 を返すため、その経路では `false` にする。
|
||||||
pub send_sampling_params: bool,
|
pub send_sampling_params: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,21 +38,21 @@ pub(crate) struct ResponsesRequest {
|
||||||
/// `["reasoning.encrypted_content"]` 等。
|
/// `["reasoning.encrypted_content"]` 等。
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||||
pub include: Vec<&'static str>,
|
pub include: Vec<&'static str>,
|
||||||
/// 公式 OpenAI Responses API では受理されるが、ChatGPT backend
|
/// 公式 OpenAI Responses API では受理されるが、互換 backend によっては
|
||||||
/// (codex-oauth) は 400 で弾く。scheme の `send_max_output_tokens`
|
/// 400 で弾く。scheme の `send_max_output_tokens` が `false` のときは
|
||||||
/// が `false` のときは `None` のまま送る (skip_serializing_if で除外)。
|
/// `None` のまま送る (skip_serializing_if で除外)。
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub max_output_tokens: Option<u32>,
|
pub max_output_tokens: Option<u32>,
|
||||||
/// 公式 OpenAI Responses API では受理されるが、ChatGPT backend
|
/// 公式 OpenAI Responses API では受理されるが、互換 backend によっては
|
||||||
/// (codex-oauth) は `temperature` / `top_p` を 400 で弾く。scheme の
|
/// `temperature` / `top_p` を 400 で弾く。scheme の
|
||||||
/// `send_sampling_params` が `false` のときは `None` のまま送る。
|
/// `send_sampling_params` が `false` のときは `None` のまま送る。
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub temperature: Option<f32>,
|
pub temperature: Option<f32>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub top_p: Option<f32>,
|
pub top_p: Option<f32>,
|
||||||
/// 会話単位の安定キー。ChatGPT backend (codex-oauth) は明示キーが
|
/// 会話単位の安定キー。明示キーを必要とする backend では、
|
||||||
/// 無いとプロンプトキャッシュがほぼ効かない。worker 側は `SegmentId`
|
/// 呼び出し側が安定した conversation identifier を渡す。
|
||||||
/// を渡す。`Request::cache_key` が `None` のときはキー自体を送らない。
|
/// `Request::cache_key` が `None` のときはキー自体を送らない。
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub prompt_cache_key: Option<String>,
|
pub prompt_cache_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
@ -74,10 +74,9 @@ pub(crate) struct ReasoningConfig {
|
||||||
#[serde(tag = "type", rename_all = "snake_case")]
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
pub(crate) enum InputItem {
|
pub(crate) enum InputItem {
|
||||||
/// 会話メッセージ。user / assistant / developer のいずれか。
|
/// 会話メッセージ。user / assistant / developer のいずれか。
|
||||||
/// `Role::System` items は `developer` として投影する(ChatGPT
|
/// `Role::System` items は `developer` として投影する。OpenAI
|
||||||
/// backend が `role: "system"` を拒否するため。Codex CLI も
|
/// Responses 互換 backend の一部は `role: "system"` を拒否するため、
|
||||||
/// system 相当の挿入には DeveloperInstructions = `role: "developer"`
|
/// system 相当の挿入には `role: "developer"` を使う。
|
||||||
/// を使う)。
|
|
||||||
Message {
|
Message {
|
||||||
role: &'static str,
|
role: &'static str,
|
||||||
content: Vec<InputContent>,
|
content: Vec<InputContent>,
|
||||||
|
|
@ -403,10 +402,9 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn system_role_item_is_projected_as_developer() {
|
fn system_role_item_is_projected_as_developer() {
|
||||||
// ChatGPT backend (codex-oauth) は input[] の `role: "system"` を
|
// Some compatible backends reject `role: "system"` in input[].
|
||||||
// "System messages are not allowed" で 400 拒否する。in-conversation
|
// Project in-conversation system notes as `role: "developer"` so
|
||||||
// な system note (notify / fs_view auto-read / compaction summary) は
|
// both official and compatible backends can accept them.
|
||||||
// `role: "developer"` として投影し、両 backend で受理されるようにする。
|
|
||||||
let scheme = OpenAIResponsesScheme::new();
|
let scheme = OpenAIResponsesScheme::new();
|
||||||
let req = Request::new()
|
let req = Request::new()
|
||||||
.user("hi")
|
.user("hi")
|
||||||
|
|
@ -523,11 +521,9 @@ mod tests {
|
||||||
fn reasoning_summary_field_is_always_serialized() {
|
fn reasoning_summary_field_is_always_serialized() {
|
||||||
// Responses API は reasoning item に `summary` を必須で要求する。
|
// Responses API は reasoning item に `summary` を必須で要求する。
|
||||||
// summary が空でも wire 上に `summary: []` として残らないと、
|
// summary が空でも wire 上に `summary: []` として残らないと、
|
||||||
// ChatGPT backend (codex-oauth) が
|
// backend によっては missing required parameter として拒否される。
|
||||||
// 400 invalid_request_error: Missing required parameter:
|
// reasoning effort 未指定のターンでは summary text が付かないことが
|
||||||
// 'input[N].summary'.
|
// あるため、空のままでも skip しないこと。
|
||||||
// で弾く。GPT-5 + reasoning effort 未指定のターンでは summary text
|
|
||||||
// が付かないことがあるため、空のままでも skip しないこと。
|
|
||||||
let scheme = OpenAIResponsesScheme::new();
|
let scheme = OpenAIResponsesScheme::new();
|
||||||
let item = Item::reasoning("").with_encrypted_content("ENC");
|
let item = Item::reasoning("").with_encrypted_content("ENC");
|
||||||
let req = Request::new().user("hi").item(item);
|
let req = Request::new().user("hi").item(item);
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,8 @@ impl Scheme for OpenAIResponsesScheme {
|
||||||
type State = OpenAIResponsesState;
|
type State = OpenAIResponsesState;
|
||||||
|
|
||||||
fn default_base_url(&self) -> &'static str {
|
fn default_base_url(&self) -> &'static str {
|
||||||
// `/v1` は base_url 側に寄せる。ChatGPT OAuth 経由のときは
|
// `/v1` は base_url 側に寄せる。互換 backend を使う場合も、
|
||||||
// `https://chatgpt.com/backend-api/codex` を base にすれば同じ
|
// base URL を差し替えるだけで同じ `/responses` path を使える。
|
||||||
// `/responses` path で両系統を吸収できる(Codex CLI 準拠)。
|
|
||||||
"https://api.openai.com/v1"
|
"https://api.openai.com/v1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,16 +58,18 @@ impl Scheme for OpenAIResponsesScheme {
|
||||||
|
|
||||||
fn validate_config(&self, config: &RequestConfig) -> Vec<ConfigWarning> {
|
fn validate_config(&self, config: &RequestConfig) -> Vec<ConfigWarning> {
|
||||||
let mut warnings = Vec::new();
|
let mut warnings = Vec::new();
|
||||||
// ChatGPT backend (codex-oauth) は `max_output_tokens` を 400 で弾く。
|
// Some compatible backends reject `max_output_tokens` with HTTP 400.
|
||||||
// scheme 構築時に `send_max_output_tokens=false` で組まれていれば
|
// If the scheme was built with `send_max_output_tokens=false`, body
|
||||||
// body 投影は止まっているので、ユーザの意図が落ちることだけを通知する。
|
// projection is already disabled; only notify that the user's intent
|
||||||
|
// was dropped.
|
||||||
if !self.send_max_output_tokens && config.max_tokens.is_some() {
|
if !self.send_max_output_tokens && config.max_tokens.is_some() {
|
||||||
warnings.push(ConfigWarning::unsupported(
|
warnings.push(ConfigWarning::unsupported(
|
||||||
"max_tokens",
|
"max_tokens",
|
||||||
"OpenAI Responses (ChatGPT backend)",
|
"OpenAI Responses (ChatGPT backend)",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// 同上、`temperature` / `top_p` も ChatGPT backend では 400 で弾かれる。
|
// Same for `temperature` / `top_p` on compatible backends that
|
||||||
|
// reject unsupported sampling parameters.
|
||||||
if !self.send_sampling_params {
|
if !self.send_sampling_params {
|
||||||
if config.temperature.is_some() {
|
if config.temperature.is_some() {
|
||||||
warnings.push(ConfigWarning::unsupported(
|
warnings.push(ConfigWarning::unsupported(
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,11 @@ use super::types::{Request, RequestConfig};
|
||||||
pub const DEFAULT_STREAM_OPEN_TIMEOUT: Duration = Duration::from_secs(20);
|
pub const DEFAULT_STREAM_OPEN_TIMEOUT: Duration = Duration::from_secs(20);
|
||||||
pub const DEFAULT_FIRST_STREAM_EVENT_TIMEOUT: Duration = Duration::from_secs(30);
|
pub const DEFAULT_FIRST_STREAM_EVENT_TIMEOUT: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
/// `AuthRef` を解決したランタイム表現。`crates/provider` が構築する。
|
/// 認証設定をリクエスト時に使える形へ解決したランタイム表現。
|
||||||
///
|
///
|
||||||
/// - `None`: 認証ヘッダを送らない(Ollama 等の opt-out)
|
/// - `None`: 認証ヘッダを送らない(Ollama 等の opt-out)
|
||||||
/// - `ApiKey`: 静的な API key 文字列
|
/// - `ApiKey`: 静的な API key 文字列
|
||||||
/// - `Custom`: リクエスト毎に動的にヘッダを組み立てる(Codex OAuth 等)
|
/// - `Custom`: リクエスト毎に動的にヘッダを組み立てる
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ResolvedAuth {
|
pub enum ResolvedAuth {
|
||||||
None,
|
None,
|
||||||
|
|
@ -226,9 +226,8 @@ impl<S: Scheme> HttpTransport<S> {
|
||||||
let value = HeaderValue::from_str(cache_key).map_err(|e| {
|
let value = HeaderValue::from_str(cache_key).map_err(|e| {
|
||||||
ClientError::Config(format!("invalid Codex conversation header: {e}"))
|
ClientError::Config(format!("invalid Codex conversation header: {e}"))
|
||||||
})?;
|
})?;
|
||||||
// Codex CLI sends hyphenated session/thread headers to the
|
// Send both current hyphenated conversation headers and the
|
||||||
// ChatGPT Codex backend. Keep the legacy underscore header for
|
// legacy underscore form for compatibility with existing backends.
|
||||||
// existing traces/backends while exposing the current Codex shape.
|
|
||||||
headers.insert(HeaderName::from_static("session-id"), value.clone());
|
headers.insert(HeaderName::from_static("session-id"), value.clone());
|
||||||
headers.insert(HeaderName::from_static("thread-id"), value.clone());
|
headers.insert(HeaderName::from_static("thread-id"), value.clone());
|
||||||
headers.insert(HeaderName::from_static("session_id"), value.clone());
|
headers.insert(HeaderName::from_static("session_id"), value.clone());
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
//! LLM Client Common Types
|
//! LLM Client Common Types
|
||||||
//!
|
//!
|
||||||
//! Core conversation types for yoi's LLM interaction model.
|
//! Core conversation types for LLM interaction.
|
||||||
//! The core abstraction is `Item` which represents different types of conversation elements:
|
//! The core abstraction is `Item` which represents different types of conversation elements:
|
||||||
//! - Message items (user/assistant messages with content parts)
|
//! - Message items (user/assistant messages with content parts)
|
||||||
//! - ToolCall items (tool invocations)
|
//! - ToolCall items (tool invocations)
|
||||||
|
|
@ -521,9 +521,8 @@ pub struct Request {
|
||||||
/// Providers without prompt caching ignore the field.
|
/// Providers without prompt caching ignore the field.
|
||||||
pub cache_anchor: Option<usize>,
|
pub cache_anchor: Option<usize>,
|
||||||
/// 会話単位の安定キー。`prompt_cache_key` として送られる
|
/// 会話単位の安定キー。`prompt_cache_key` として送られる
|
||||||
/// (OpenAI Responses)。ChatGPT backend (codex-oauth) は明示キーが
|
/// (OpenAI Responses)。明示キーを必要とする backend では、
|
||||||
/// 無いと org/project ハッシュ衝突でプロンプトキャッシュが
|
/// 呼び出し側が安定した conversation identifier を渡す。
|
||||||
/// ほぼヒットしないため、worker 側で `SegmentId` を渡す運用を想定。
|
|
||||||
/// `cache_anchor` と違い名前空間キーであり、`prefix anchor` とは
|
/// `cache_anchor` と違い名前空間キーであり、`prefix anchor` とは
|
||||||
/// 別の概念。`cache_anchor` を読まない provider と同じく、
|
/// 別の概念。`cache_anchor` を読まない provider と同じく、
|
||||||
/// `prompt_cache_key` を持たない provider は無視する。
|
/// `prompt_cache_key` を持たない provider は無視する。
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
//!
|
//!
|
||||||
//! Prune は **コンテキスト射影** であり、history の変換ではない。
|
//! Prune は **コンテキスト射影** であり、history の変換ではない。
|
||||||
//! この crate が提供するのは pure な候補抽出 [`prunable_indices`] のみで、
|
//! この crate が提供するのは pure な候補抽出 [`prunable_indices`] のみで、
|
||||||
//! 射影の適用は上位層(`worker::prune_hook` 等)が LLM に送る一時コンテキスト
|
//! 射影の適用は LLM に送る一時コンテキストに対してだけ行う。
|
||||||
//! に対してだけ行う。Engine の永続履歴は決して変更されない。
|
//! Engine の永続履歴は決して変更されない。
|
||||||
//!
|
//!
|
||||||
//! 保護境界は末尾 token budget で決めるが、この crate は usage 履歴を
|
//! 保護境界は末尾 token budget で決めるが、この crate は usage 履歴を
|
||||||
//! 所有しない。prefix ごとの token 推定値と savings 推定は上位層から
|
//! 所有しない。prefix ごとの token 推定値と savings 推定は上位層から
|
||||||
|
|
@ -75,7 +75,7 @@ pub enum PruneDecision {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional observer invoked after each prune evaluation, regardless of
|
/// Optional observer invoked after each prune evaluation, regardless of
|
||||||
/// branch. Worker 等の上位層が install して metrics を発行する。
|
/// branch. Callers can install this to publish metrics or traces.
|
||||||
pub type PruneObserver = Box<dyn Fn(&PruneEvaluation) + Send + Sync>;
|
pub type PruneObserver = Box<dyn Fn(&PruneEvaluation) + Send + Sync>;
|
||||||
|
|
||||||
/// Configuration for the Prune algorithm.
|
/// Configuration for the Prune algorithm.
|
||||||
|
|
|
||||||
|
|
@ -75,10 +75,9 @@ impl ToolServerHandle {
|
||||||
/// Execute all pending factories and register the resulting tools.
|
/// Execute all pending factories and register the resulting tools.
|
||||||
///
|
///
|
||||||
/// Called implicitly by `Engine::lock()` before the first turn.
|
/// Called implicitly by `Engine::lock()` before the first turn.
|
||||||
/// Exposed as `pub` so higher layers (e.g. Worker) can force-materialise
|
/// Exposed as `pub` so callers can force-materialise tools earlier —
|
||||||
/// tools earlier — for example when building a system-prompt template
|
/// for example when building a system-prompt template context that needs
|
||||||
/// context that needs the list of registered tool names. Redundant
|
/// the list of registered tool names. Redundant calls are no-ops.
|
||||||
/// calls are no-ops.
|
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! 1 リクエストの送信時点での「ある history prefix 長で計測した占有量」を
|
//! 1 リクエストの送信時点での「ある history prefix 長で計測した占有量」を
|
||||||
//! 1 件分にまとめたもの。`UsageEvent` (provider stream イベント) を
|
//! 1 件分にまとめたもの。`UsageEvent` (provider stream イベント) を
|
||||||
//! 受けて呼び出し側 (typically Worker) が組み立て、永続化層
|
//! 受けて呼び出し側が組み立て、永続化層に流したり、token accounting
|
||||||
//! (session-store) に流したり、token accounting (`token_counter`) で
|
//! (`token_counter`) で履歴として参照したりする。
|
||||||
//! 履歴として参照したりする。
|
|
||||||
|
|
||||||
/// LLM リクエスト送信時点での占有量スナップショット。
|
/// LLM リクエスト送信時点での占有量スナップショット。
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user