feat: OpenResponses
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# Worker API/DSL 実装計画
|
||||
|
||||
## 目的
|
||||
|
||||
- [Open Responses](https://www.openresponses.org)(以後"OR")に準拠した正規化を前提に、
|
||||
Item/Part の2段スコープを扱える Worker API を設計する。
|
||||
- APIの煩雑化を防ぐため、worker.on_xxx として公開するのを避けつつ、
|
||||
Text/Thinking/Tool など型の違いを静的に扱える DSL を提供する。
|
||||
|
||||
## 方針
|
||||
|
||||
- 内部は Timeline が Event を正規化し、Item/Part/Meta
|
||||
を単一ストリームとして扱う。
|
||||
- API では Item/Part 型ごとに ctx を持てるようにし、DSL
|
||||
で記述の冗長さを削減する。
|
||||
- まず macro_rules! 版を作り、必要なら proc-macro に拡張する。
|
||||
- Item/Part の型パラメータはクレートが公開する Kind 型を使う。
|
||||
|
||||
## 仕様の前提
|
||||
|
||||
- Item は OR の item (message, function_call, reasoning など) に対応する。
|
||||
- Part は OR の content part (output_text, reasoning_text など) に対応する。
|
||||
- Item は必ず start/stop を持つ。Part は Item 内で複数発生し得る。
|
||||
- Item/Part の型指定は `Item<Message>` / `Part<ReasoningText>` のように書く。
|
||||
|
||||
## 設計ステップ
|
||||
|
||||
### 1. 内部イベントモデルの整理
|
||||
|
||||
- Event を Item/Part/Meta の3層に整理する。
|
||||
- ItemEvent / PartEvent は型パラメータで区別する。
|
||||
- 例: ItemEvent<Message>, PartEvent<Message, OutputText>
|
||||
|
||||
### 2. スコープの二段化
|
||||
|
||||
- Item ctx: Item 型ごとに1つ
|
||||
- Part ctx: Part 型ごとに1つ
|
||||
- Part のイベントでは常に item ctx と part ctx の両方を渡す。
|
||||
|
||||
### 3. Handler trait の再定義
|
||||
|
||||
- Item/Part を型で指定できる trait を導入する。
|
||||
- 例:
|
||||
- trait ItemHandler<I>
|
||||
- trait PartHandler<I, P>
|
||||
- PartHandler には ItemHandler の ItemCtx を必須で渡す。
|
||||
- Part の ctx 型は `PartKind::Ctx` 方式 or enum 方式で切り替える。
|
||||
|
||||
### 4. Timeline との結合
|
||||
|
||||
- Timeline は ItemStart で ItemCtx を生成
|
||||
- PartStart で PartCtx を生成
|
||||
- Delta/Stop は対応 ctx に流す
|
||||
- ItemStop で ItemCtx を破棄
|
||||
|
||||
### 5. DSL (macro_rules!) の導入
|
||||
|
||||
- まず宣言的 DSL を提供する。
|
||||
- 例:
|
||||
- handler! { Item<Message> { type ItemCtx = ...; Part<OutputText> { type
|
||||
PartCtx = ...; } } }
|
||||
- DSL は ItemHandler / PartHandler 実装を生成する。
|
||||
- Item/Part の Kind 型はクレートが公開する型を参照する。
|
||||
|
||||
### 6. 拡張ポイント
|
||||
|
||||
- 追加 Part (output_image など) を DSL に追加しやすい形にする。
|
||||
- 必要なら proc-macro に移行して構文自由度を上げる。
|
||||
|
||||
## 実装順序
|
||||
|
||||
1. Event/Item/Part の型定義の整理
|
||||
2. Item/Part ctx を持つ Timeline 実装
|
||||
3. Handler trait の定義・既存コードの移行
|
||||
4. macro_rules! DSL の実装
|
||||
5. 既存ユースケースの移植
|
||||
|
||||
## TODO
|
||||
|
||||
- Item と Part の型対応表を整理する
|
||||
- OR と既存 llm_client の差分を再確認する
|
||||
- Tool args の delta を OR 拡張として扱うか検討する
|
||||
- macro_rules! で表現可能な DSL の最小文法を確定する
|
||||
@@ -0,0 +1,80 @@
|
||||
# Open Responses mapping (llm_client -> Open Responses)
|
||||
|
||||
This document maps the current `llm_client` event model to Open Responses items
|
||||
and streaming events. It focuses on output streaming; input items are noted
|
||||
where they are the closest semantic match.
|
||||
|
||||
## Legend
|
||||
|
||||
- **OR item**: Open Responses item types used in `response.output`.
|
||||
- **OR event**: Open Responses streaming events (`response.*`).
|
||||
- **Note**: Gaps or required adaptation decisions.
|
||||
|
||||
## Response lifecycle / meta events
|
||||
|
||||
| llm_client | Open Responses | Note |
|
||||
| ------------------------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `StatusEvent::Started` | `response.created`, `response.queued`, `response.in_progress` | OR has finer-grained lifecycle states; pick a subset or map Started -> `response.in_progress`. |
|
||||
| `StatusEvent::Completed` | `response.completed` | |
|
||||
| `StatusEvent::Failed` | `response.failed` | |
|
||||
| `StatusEvent::Cancelled` | (no direct event) | Could map to `response.incomplete` or `response.failed` depending on semantics. |
|
||||
| `UsageEvent` | `response.completed` payload usage | OR reports usage on the response object, not as a dedicated streaming event. |
|
||||
| `ErrorEvent` | `error` event | OR has a dedicated error streaming event. |
|
||||
| `PingEvent` | (no direct event) | OR does not define a heartbeat event. |
|
||||
|
||||
## Output block lifecycle
|
||||
|
||||
### Text block
|
||||
|
||||
| llm_client | Open Responses | Note |
|
||||
| ------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| `BlockStart { block_type: Text, metadata: Text }` | `response.output_item.added` with item type `message` (assistant) | OR output items are message/function_call/reasoning. This creates the message item. |
|
||||
| `BlockDelta { delta: Text(..) }` | `response.output_text.delta` | Text deltas map 1:1 to output text deltas. |
|
||||
| `BlockStop { block_type: Text }` | `response.output_text.done` + `response.content_part.done` + `response.output_item.done` | OR emits separate done events for content parts and items. |
|
||||
|
||||
### Tool use (function call)
|
||||
|
||||
| llm_client | Open Responses | Note |
|
||||
| -------------------------------------------------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
||||
| `BlockStart { block_type: ToolUse, metadata: ToolUse { id, name } }` | `response.output_item.added` with item type `function_call` | OR uses `call_id` + `name` + `arguments` string. Map `id` -> `call_id`. |
|
||||
| `BlockDelta { delta: InputJson(..) }` | `response.function_call_arguments.delta` | OR spec does not explicitly require argument deltas; treat as OpenAI-compatible extension if adopted. |
|
||||
| `BlockStop { block_type: ToolUse }` | `response.function_call_arguments.done` + `response.output_item.done` | Item status can be set to `completed` or `incomplete`. |
|
||||
|
||||
### Tool result (function call output)
|
||||
|
||||
| llm_client | Open Responses | Note |
|
||||
| ----------------------------------------------------------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| `BlockStart { block_type: ToolResult, metadata: ToolResult { tool_use_id } }` | **Input item** `function_call_output` | OR treats tool results as input items, not output items. This is a request-side mapping. |
|
||||
| `BlockDelta` | (no direct output event) | OR does not stream tool output deltas as response events. |
|
||||
| `BlockStop` | (no direct output event) | Tool output lives on the next request as an input item. |
|
||||
|
||||
### Thinking / reasoning
|
||||
|
||||
| llm_client | Open Responses | Note |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `BlockStart { block_type: Thinking, metadata: Thinking }` | `response.output_item.added` with item type `reasoning` | OR models reasoning as a separate item type. |
|
||||
| `BlockDelta { delta: Thinking(..) }` | `response.reasoning.delta` | OR has dedicated reasoning delta events. |
|
||||
| `BlockStop { block_type: Thinking }` | `response.reasoning.done` | OR separates reasoning summary events (`response.reasoning_summary_*`) from reasoning deltas. Decide whether Thinking maps to full reasoning or summary only. |
|
||||
|
||||
## Stop reasons
|
||||
|
||||
| llm_client `StopReason` | Open Responses | Note |
|
||||
| ----------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------- |
|
||||
| `EndTurn` | `response.completed` + item status `completed` | |
|
||||
| `MaxTokens` | `response.incomplete` + item status `incomplete` | |
|
||||
| `StopSequence` | `response.completed` | |
|
||||
| `ToolUse` | `response.completed` for message item, followed by `function_call` output item | OR models tool call as a separate output item. |
|
||||
|
||||
## Gaps / open decisions
|
||||
|
||||
- `PingEvent` has no OR equivalent. If needed, keep as internal only.
|
||||
- `Cancelled` status needs a policy: map to `response.incomplete` or
|
||||
`response.failed`.
|
||||
- OR has `response.refusal.delta` / `response.refusal.done`. `llm_client` has no
|
||||
refusal delta type; consider adding a new block or delta variant if needed.
|
||||
- OR splits _item_ and _content part_ lifecycles. `llm_client` currently has a
|
||||
single block lifecycle, so mapping should decide whether to synthesize
|
||||
`content_part.*` events or ignore them.
|
||||
- The OR specification does not state how `function_call.arguments` stream
|
||||
deltas; `response.function_call_arguments.*` should be treated as a compatible
|
||||
extension if required.
|
||||
Reference in New Issue
Block a user