支持reasoning
This commit is contained in:
@@ -92,56 +92,25 @@ curl http://127.0.0.1:18000/v1/chat/completions \
|
|||||||
<!-- gitnexus:start -->
|
<!-- gitnexus:start -->
|
||||||
# GitNexus — Code Intelligence
|
# GitNexus — Code Intelligence
|
||||||
|
|
||||||
This project is indexed by GitNexus as **command-code-openai-bridge** (106 symbols, 201 relationships, 9 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
This project is indexed by GitNexus as **command-code-openai-bridge** (428 symbols, 1186 relationships, 33 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
|
||||||
|
|
||||||
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.
|
> Index stale? Run `node .gitnexus/run.cjs analyze` from the project root — it auto-selects an available runner. No `.gitnexus/run.cjs` yet? `npx gitnexus analyze` (npm 11 crash → `npm i -g gitnexus`; #1939).
|
||||||
|
|
||||||
## Always Do
|
## Always Do
|
||||||
|
|
||||||
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `gitnexus_impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
|
- **MUST run impact analysis before editing any symbol.** Before modifying a function, class, or method, run `impact({target: "symbolName", direction: "upstream"})` and report the blast radius (direct callers, affected processes, risk level) to the user.
|
||||||
- **MUST run `gitnexus_detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows.
|
- **MUST run `detect_changes()` before committing** to verify your changes only affect expected symbols and execution flows. For regression review, compare against the default branch: `detect_changes({scope: "compare", base_ref: "main"})`.
|
||||||
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
|
- **MUST warn the user** if impact analysis returns HIGH or CRITICAL risk before proceeding with edits.
|
||||||
- When exploring unfamiliar code, use `gitnexus_query({query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
|
- When exploring unfamiliar code, use `query({search_query: "concept"})` to find execution flows instead of grepping. It returns process-grouped results ranked by relevance.
|
||||||
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `gitnexus_context({name: "symbolName"})`.
|
- When you need full context on a specific symbol — callers, callees, which execution flows it participates in — use `context({name: "symbolName"})`.
|
||||||
|
- For security review, `explain({target: "fileOrSymbol"})` lists taint findings (source→sink flows; needs `analyze --pdg`).
|
||||||
## When Debugging
|
|
||||||
|
|
||||||
1. `gitnexus_query({query: "<error or symptom>"})` — find execution flows related to the issue
|
|
||||||
2. `gitnexus_context({name: "<suspect function>"})` — see all callers, callees, and process participation
|
|
||||||
3. `READ gitnexus://repo/command-code-openai-bridge/process/{processName}` — trace the full execution flow step by step
|
|
||||||
4. For regressions: `gitnexus_detect_changes({scope: "compare", base_ref: "main"})` — see what your branch changed
|
|
||||||
|
|
||||||
## When Refactoring
|
|
||||||
|
|
||||||
- **Renaming**: MUST use `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` first. Review the preview — graph edits are safe, text_search edits need manual review. Then run with `dry_run: false`.
|
|
||||||
- **Extracting/Splitting**: MUST run `gitnexus_context({name: "target"})` to see all incoming/outgoing refs, then `gitnexus_impact({target: "target", direction: "upstream"})` to find all external callers before moving code.
|
|
||||||
- After any refactor: run `gitnexus_detect_changes({scope: "all"})` to verify only expected files changed.
|
|
||||||
|
|
||||||
## Never Do
|
## Never Do
|
||||||
|
|
||||||
- NEVER edit a function, class, or method without first running `gitnexus_impact` on it.
|
- NEVER edit a function, class, or method without first running `impact` on it.
|
||||||
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
|
- NEVER ignore HIGH or CRITICAL risk warnings from impact analysis.
|
||||||
- NEVER rename symbols with find-and-replace — use `gitnexus_rename` which understands the call graph.
|
- NEVER rename symbols with find-and-replace — use `rename` which understands the call graph.
|
||||||
- NEVER commit changes without running `gitnexus_detect_changes()` to check affected scope.
|
- NEVER commit changes without running `detect_changes()` to check affected scope.
|
||||||
|
|
||||||
## Tools Quick Reference
|
|
||||||
|
|
||||||
| Tool | When to use | Command |
|
|
||||||
|------|-------------|---------|
|
|
||||||
| `query` | Find code by concept | `gitnexus_query({query: "auth validation"})` |
|
|
||||||
| `context` | 360-degree view of one symbol | `gitnexus_context({name: "validateUser"})` |
|
|
||||||
| `impact` | Blast radius before editing | `gitnexus_impact({target: "X", direction: "upstream"})` |
|
|
||||||
| `detect_changes` | Pre-commit scope check | `gitnexus_detect_changes({scope: "staged"})` |
|
|
||||||
| `rename` | Safe multi-file rename | `gitnexus_rename({symbol_name: "old", new_name: "new", dry_run: true})` |
|
|
||||||
| `cypher` | Custom graph queries | `gitnexus_cypher({query: "MATCH ..."})` |
|
|
||||||
|
|
||||||
## Impact Risk Levels
|
|
||||||
|
|
||||||
| Depth | Meaning | Action |
|
|
||||||
|-------|---------|--------|
|
|
||||||
| d=1 | WILL BREAK — direct callers/importers | MUST update these |
|
|
||||||
| d=2 | LIKELY AFFECTED — indirect deps | Should test |
|
|
||||||
| d=3 | MAY NEED TESTING — transitive | Test if critical path |
|
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
@@ -152,32 +121,6 @@ This project is indexed by GitNexus as **command-code-openai-bridge** (106 symbo
|
|||||||
| `gitnexus://repo/command-code-openai-bridge/processes` | All execution flows |
|
| `gitnexus://repo/command-code-openai-bridge/processes` | All execution flows |
|
||||||
| `gitnexus://repo/command-code-openai-bridge/process/{name}` | Step-by-step execution trace |
|
| `gitnexus://repo/command-code-openai-bridge/process/{name}` | Step-by-step execution trace |
|
||||||
|
|
||||||
## Self-Check Before Finishing
|
|
||||||
|
|
||||||
Before completing any code modification task, verify:
|
|
||||||
1. `gitnexus_impact` was run for all modified symbols
|
|
||||||
2. No HIGH/CRITICAL risk warnings were ignored
|
|
||||||
3. `gitnexus_detect_changes()` confirms changes match expected scope
|
|
||||||
4. All d=1 (WILL BREAK) dependents were updated
|
|
||||||
|
|
||||||
## Keeping the Index Fresh
|
|
||||||
|
|
||||||
After committing code changes, the GitNexus index becomes stale. Re-run analyze to update it:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx gitnexus analyze
|
|
||||||
```
|
|
||||||
|
|
||||||
If the index previously included embeddings, preserve them by adding `--embeddings`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npx gitnexus analyze --embeddings
|
|
||||||
```
|
|
||||||
|
|
||||||
To check whether embeddings exist, inspect `.gitnexus/meta.json` — the `stats.embeddings` field shows the count (0 means no embeddings). **Running analyze without `--embeddings` will delete any previously generated embeddings.**
|
|
||||||
|
|
||||||
> Claude Code users: A PostToolUse hook handles this automatically after `git commit` and `git merge`.
|
|
||||||
|
|
||||||
## CLI
|
## CLI
|
||||||
|
|
||||||
| Task | Read this skill file |
|
| Task | Read this skill file |
|
||||||
|
|||||||
+197
-22
@@ -227,7 +227,20 @@ interface ResponseOutputFunctionCall {
|
|||||||
status: "completed" | "incomplete";
|
status: "completed" | "incomplete";
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResponseOutputItem = ResponseOutputMessage | ResponseOutputFunctionCall;
|
interface ResponseReasoningSummaryText {
|
||||||
|
type: "summary_text";
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResponseOutputReasoning {
|
||||||
|
id: string;
|
||||||
|
type: "reasoning";
|
||||||
|
summary: ResponseReasoningSummaryText[];
|
||||||
|
status: "in_progress" | "completed" | "incomplete";
|
||||||
|
content?: Array<{ type: "reasoning_text"; text: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseOutputItem = ResponseOutputMessage | ResponseOutputFunctionCall | ResponseOutputReasoning;
|
||||||
|
|
||||||
interface ResponseUsage {
|
interface ResponseUsage {
|
||||||
input_tokens: number;
|
input_tokens: number;
|
||||||
@@ -285,11 +298,13 @@ interface StructuredValidation {
|
|||||||
interface AttemptResult {
|
interface AttemptResult {
|
||||||
result: CommandResult;
|
result: CommandResult;
|
||||||
finalChunks: string[];
|
finalChunks: string[];
|
||||||
|
reasoningItems: ResponseOutputReasoning[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExecutionResult {
|
interface ExecutionResult {
|
||||||
finalText: string;
|
finalText: string;
|
||||||
chunks: string[];
|
chunks: string[];
|
||||||
|
reasoningItems: ResponseOutputReasoning[];
|
||||||
usage?: CommandUsage;
|
usage?: CommandUsage;
|
||||||
validationErrors?: string[];
|
validationErrors?: string[];
|
||||||
toolCalls?: FunctionToolCall[];
|
toolCalls?: FunctionToolCall[];
|
||||||
@@ -411,10 +426,72 @@ class ResponseStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ResponseReasoningAccumulator {
|
||||||
|
private readonly completed: ResponseOutputReasoning[] = [];
|
||||||
|
private current: { id: string; outputIndex: number; text: string } | undefined;
|
||||||
|
|
||||||
|
constructor(private readonly writer?: ResponsesSseWriter) {}
|
||||||
|
|
||||||
|
event(event: Record<string, unknown>): void {
|
||||||
|
if (event.type === "thinking_start") {
|
||||||
|
this.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (event.type === "thinking_delta" && typeof event.delta === "string") {
|
||||||
|
this.add(event.delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
event.type === "thinking_end"
|
||||||
|
|| event.type === "turn_start"
|
||||||
|
|| event.type === "text_delta"
|
||||||
|
|| event.type === "message_end"
|
||||||
|
|| event.type === "turn_end"
|
||||||
|
) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finish(): ResponseOutputReasoning[] {
|
||||||
|
this.close();
|
||||||
|
return [...this.completed];
|
||||||
|
}
|
||||||
|
|
||||||
|
private add(delta: string): void {
|
||||||
|
if (delta === "") return;
|
||||||
|
if (!this.current) {
|
||||||
|
this.current = {
|
||||||
|
id: newReasoningItemId(),
|
||||||
|
outputIndex: this.completed.length,
|
||||||
|
text: "",
|
||||||
|
};
|
||||||
|
this.writer?.beginReasoning(this.current.id, this.current.outputIndex);
|
||||||
|
}
|
||||||
|
this.current.text += delta;
|
||||||
|
this.writer?.addReasoningDelta(this.current.id, this.current.outputIndex, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
private close(): void {
|
||||||
|
if (!this.current) return;
|
||||||
|
const item: ResponseOutputReasoning = {
|
||||||
|
id: this.current.id,
|
||||||
|
type: "reasoning",
|
||||||
|
summary: [{ type: "summary_text", text: this.current.text }],
|
||||||
|
status: "completed",
|
||||||
|
};
|
||||||
|
this.completed.push(item);
|
||||||
|
this.writer?.finishReasoning(item, this.current.outputIndex);
|
||||||
|
this.current = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ResponsesSseWriter {
|
class ResponsesSseWriter {
|
||||||
private sequenceNumber = 0;
|
private sequenceNumber = 0;
|
||||||
private outputStarted = false;
|
private outputStarted = false;
|
||||||
private outputText = "";
|
private outputText = "";
|
||||||
|
private textOutputIndex: number | undefined;
|
||||||
|
private readonly emittedReasoningIds = new Set<string>();
|
||||||
|
private readonly completedReasoning = new Map<string, ResponseOutputReasoning>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly response: ServerResponse,
|
private readonly response: ServerResponse,
|
||||||
@@ -432,11 +509,72 @@ class ResponsesSseWriter {
|
|||||||
this.event("response.in_progress", { response: initial });
|
this.event("response.in_progress", { response: initial });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
beginReasoning(itemId: string, outputIndex: number): void {
|
||||||
|
if (this.emittedReasoningIds.has(itemId)) return;
|
||||||
|
this.emittedReasoningIds.add(itemId);
|
||||||
|
this.event("response.output_item.added", {
|
||||||
|
output_index: outputIndex,
|
||||||
|
item: {
|
||||||
|
id: itemId,
|
||||||
|
type: "reasoning",
|
||||||
|
summary: [],
|
||||||
|
status: "in_progress",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.event("response.reasoning_summary_part.added", {
|
||||||
|
item_id: itemId,
|
||||||
|
output_index: outputIndex,
|
||||||
|
summary_index: 0,
|
||||||
|
part: { type: "summary_text", text: "" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addReasoningDelta(itemId: string, outputIndex: number, delta: string): void {
|
||||||
|
if (delta === "" || !this.emittedReasoningIds.has(itemId)) return;
|
||||||
|
this.event("response.reasoning_summary_text.delta", {
|
||||||
|
item_id: itemId,
|
||||||
|
output_index: outputIndex,
|
||||||
|
summary_index: 0,
|
||||||
|
delta,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
finishReasoning(item: ResponseOutputReasoning, outputIndex: number): void {
|
||||||
|
if (this.completedReasoning.has(item.id)) return;
|
||||||
|
const part = item.summary[0];
|
||||||
|
if (!part) return;
|
||||||
|
this.completedReasoning.set(item.id, item);
|
||||||
|
this.event("response.reasoning_summary_text.done", {
|
||||||
|
item_id: item.id,
|
||||||
|
output_index: outputIndex,
|
||||||
|
summary_index: 0,
|
||||||
|
text: part.text,
|
||||||
|
});
|
||||||
|
this.event("response.reasoning_summary_part.done", {
|
||||||
|
item_id: item.id,
|
||||||
|
output_index: outputIndex,
|
||||||
|
summary_index: 0,
|
||||||
|
part,
|
||||||
|
});
|
||||||
|
this.event("response.output_item.done", { output_index: outputIndex, item });
|
||||||
|
}
|
||||||
|
|
||||||
|
addReasoningItems(items: ResponseOutputReasoning[]): void {
|
||||||
|
for (const [outputIndex, item] of items.entries()) {
|
||||||
|
if (this.emittedReasoningIds.has(item.id)) continue;
|
||||||
|
this.beginReasoning(item.id, outputIndex);
|
||||||
|
const part = item.summary[0];
|
||||||
|
if (part?.text) this.addReasoningDelta(item.id, outputIndex, part.text);
|
||||||
|
this.finishReasoning(item, outputIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
addText(chunks: string[]): void {
|
addText(chunks: string[]): void {
|
||||||
if (!this.outputStarted) {
|
if (!this.outputStarted) {
|
||||||
this.outputStarted = true;
|
this.outputStarted = true;
|
||||||
|
this.textOutputIndex = this.completedReasoning.size;
|
||||||
this.event("response.output_item.added", {
|
this.event("response.output_item.added", {
|
||||||
output_index: 0,
|
output_index: this.textOutputIndex,
|
||||||
item: {
|
item: {
|
||||||
id: this.messageId,
|
id: this.messageId,
|
||||||
type: "message",
|
type: "message",
|
||||||
@@ -447,7 +585,7 @@ class ResponsesSseWriter {
|
|||||||
});
|
});
|
||||||
this.event("response.content_part.added", {
|
this.event("response.content_part.added", {
|
||||||
item_id: this.messageId,
|
item_id: this.messageId,
|
||||||
output_index: 0,
|
output_index: this.textOutputIndex,
|
||||||
content_index: 0,
|
content_index: 0,
|
||||||
part: { type: "output_text", text: "", annotations: [] },
|
part: { type: "output_text", text: "", annotations: [] },
|
||||||
});
|
});
|
||||||
@@ -458,7 +596,7 @@ class ResponsesSseWriter {
|
|||||||
this.outputText += delta;
|
this.outputText += delta;
|
||||||
this.event("response.output_text.delta", {
|
this.event("response.output_text.delta", {
|
||||||
item_id: this.messageId,
|
item_id: this.messageId,
|
||||||
output_index: 0,
|
output_index: this.textOutputIndex,
|
||||||
content_index: 0,
|
content_index: 0,
|
||||||
delta,
|
delta,
|
||||||
});
|
});
|
||||||
@@ -471,28 +609,38 @@ class ResponsesSseWriter {
|
|||||||
this.event(terminalEvent, { response });
|
this.event(terminalEvent, { response });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const outputIndex = response.output.findIndex((item) => (
|
||||||
|
item.type === "message" && item.id === this.messageId
|
||||||
|
));
|
||||||
|
if (outputIndex < 0) {
|
||||||
|
this.event(terminalEvent, { response });
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.ensureFinalText(response.output_text);
|
this.ensureFinalText(response.output_text);
|
||||||
if (this.outputStarted) {
|
if (this.outputStarted) {
|
||||||
const outputMessage = response.output[0];
|
const outputMessage = response.output[outputIndex];
|
||||||
if (!outputMessage || outputMessage.type !== "message") {
|
if (!outputMessage || outputMessage.type !== "message") {
|
||||||
throw new Error("Response output message is missing");
|
throw new Error("Response output message is missing");
|
||||||
}
|
}
|
||||||
|
if (this.textOutputIndex !== outputIndex) {
|
||||||
|
throw new Error("Response output message index does not match the streamed item index");
|
||||||
|
}
|
||||||
const part = outputMessage.content[0];
|
const part = outputMessage.content[0];
|
||||||
if (!part) throw new Error("Response output text part is missing");
|
if (!part) throw new Error("Response output text part is missing");
|
||||||
|
|
||||||
this.event("response.output_text.done", {
|
this.event("response.output_text.done", {
|
||||||
item_id: this.messageId,
|
item_id: this.messageId,
|
||||||
output_index: 0,
|
output_index: outputIndex,
|
||||||
content_index: 0,
|
content_index: 0,
|
||||||
text: response.output_text,
|
text: response.output_text,
|
||||||
});
|
});
|
||||||
this.event("response.content_part.done", {
|
this.event("response.content_part.done", {
|
||||||
item_id: this.messageId,
|
item_id: this.messageId,
|
||||||
output_index: 0,
|
output_index: outputIndex,
|
||||||
content_index: 0,
|
content_index: 0,
|
||||||
part,
|
part,
|
||||||
});
|
});
|
||||||
this.event("response.output_item.done", { output_index: 0, item: outputMessage });
|
this.event("response.output_item.done", { output_index: outputIndex, item: outputMessage });
|
||||||
}
|
}
|
||||||
this.event(terminalEvent, { response });
|
this.event(terminalEvent, { response });
|
||||||
}
|
}
|
||||||
@@ -709,6 +857,7 @@ export async function registerResponseRoutes(
|
|||||||
effort: effectiveEffort,
|
effort: effectiveEffort,
|
||||||
status: execution.validationErrors ? "incomplete" : "completed",
|
status: execution.validationErrors ? "incomplete" : "completed",
|
||||||
finalText: execution.finalText,
|
finalText: execution.finalText,
|
||||||
|
reasoningItems: execution.reasoningItems,
|
||||||
...(execution.toolCalls ? { toolCalls: execution.toolCalls } : {}),
|
...(execution.toolCalls ? { toolCalls: execution.toolCalls } : {}),
|
||||||
...(execution.usage ? { usage: execution.usage } : {}),
|
...(execution.usage ? { usage: execution.usage } : {}),
|
||||||
...(execution.validationErrors ? { incompleteReason: "structured_output_validation_failed" } : {}),
|
...(execution.validationErrors ? { incompleteReason: "structured_output_validation_failed" } : {}),
|
||||||
@@ -718,6 +867,7 @@ export async function registerResponseRoutes(
|
|||||||
responseCompleted = true;
|
responseCompleted = true;
|
||||||
|
|
||||||
if (writer) {
|
if (writer) {
|
||||||
|
writer.addReasoningItems(execution.reasoningItems);
|
||||||
if (validation) writer.addText(execution.chunks);
|
if (validation) writer.addText(execution.chunks);
|
||||||
writer.finish(
|
writer.finish(
|
||||||
terminalResponse,
|
terminalResponse,
|
||||||
@@ -933,6 +1083,7 @@ function buildResponsesPrompt(
|
|||||||
"你只负责决定当前这一轮应该调用外部工具还是返回最终回答。外部工具由客户端执行,你不能模拟工具结果。",
|
"你只负责决定当前这一轮应该调用外部工具还是返回最终回答。外部工具由客户端执行,你不能模拟工具结果。",
|
||||||
"不要使用 Command Code 自身的文件、终端、搜索、网络或其他工具替代外部 tools;只依据 input、tools 和 function_call_output 作出决定。",
|
"不要使用 Command Code 自身的文件、终端、搜索、网络或其他工具替代外部 tools;只依据 input、tools 和 function_call_output 作出决定。",
|
||||||
"严格执行 input 中的 system、developer、user 指令和当前 instructions,并结合 message、function_call、function_call_output 及其他已有 output item 理解完整历史。",
|
"严格执行 input 中的 system、developer、user 指令和当前 instructions,并结合 message、function_call、function_call_output 及其他已有 output item 理解完整历史。",
|
||||||
|
"历史 reasoning item 只包含此前公开的 summary 文本记录,不是可恢复的内部推理状态;仅把 summary 当作普通历史上下文。",
|
||||||
"只返回以下两个 JSON 对象之一,禁止 Markdown 代码围栏、前后说明和额外字段:",
|
"只返回以下两个 JSON 对象之一,禁止 Markdown 代码围栏、前后说明和额外字段:",
|
||||||
"最终回答:{\"type\":\"final\",\"content\":\"面向用户的完整回答\"}",
|
"最终回答:{\"type\":\"final\",\"content\":\"面向用户的完整回答\"}",
|
||||||
"调用工具:{\"type\":\"tool_calls\",\"calls\":[{\"name\":\"工具名称\",\"arguments\":{}}]}",
|
"调用工具:{\"type\":\"tool_calls\",\"calls\":[{\"name\":\"工具名称\",\"arguments\":{}}]}",
|
||||||
@@ -948,6 +1099,7 @@ function buildResponsesPrompt(
|
|||||||
return [
|
return [
|
||||||
"下面 JSON 对象是外部客户端提交的完整 Responses 文本任务和本地重建的响应链。",
|
"下面 JSON 对象是外部客户端提交的完整 Responses 文本任务和本地重建的响应链。",
|
||||||
"按 input 中的 system、developer、user、assistant 角色与顺序理解上下文;当前 instructions 是本次请求的高优先级开发者指令。",
|
"按 input 中的 system、developer、user、assistant 角色与顺序理解上下文;当前 instructions 是本次请求的高优先级开发者指令。",
|
||||||
|
"历史 reasoning item 只包含此前公开的 summary 文本记录,不是可恢复的内部推理状态;仅把 summary 当作普通历史上下文。",
|
||||||
"严格执行最后一个用户任务。不要复述 JSON,不要输出角色标签,不要暴露内部思考。",
|
"严格执行最后一个用户任务。不要复述 JSON,不要输出角色标签,不要暴露内部思考。",
|
||||||
formatDirective(request.text.format),
|
formatDirective(request.text.format),
|
||||||
"JSON 数据开始:",
|
"JSON 数据开始:",
|
||||||
@@ -1006,6 +1158,7 @@ async function executeResponse(options: {
|
|||||||
return {
|
return {
|
||||||
finalText: first.result.finalText,
|
finalText: first.result.finalText,
|
||||||
chunks: first.finalChunks,
|
chunks: first.finalChunks,
|
||||||
|
reasoningItems: first.reasoningItems,
|
||||||
...(first.result.usage ? { usage: first.result.usage } : {}),
|
...(first.result.usage ? { usage: first.result.usage } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1015,6 +1168,7 @@ async function executeResponse(options: {
|
|||||||
return {
|
return {
|
||||||
finalText: first.result.finalText,
|
finalText: first.result.finalText,
|
||||||
chunks: first.finalChunks,
|
chunks: first.finalChunks,
|
||||||
|
reasoningItems: first.reasoningItems,
|
||||||
...(first.result.usage ? { usage: first.result.usage } : {}),
|
...(first.result.usage ? { usage: first.result.usage } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1032,6 +1186,7 @@ async function executeResponse(options: {
|
|||||||
return {
|
return {
|
||||||
finalText: second.result.finalText,
|
finalText: second.result.finalText,
|
||||||
chunks: second.finalChunks,
|
chunks: second.finalChunks,
|
||||||
|
reasoningItems: second.reasoningItems,
|
||||||
...(combinedUsage ? { usage: combinedUsage } : {}),
|
...(combinedUsage ? { usage: combinedUsage } : {}),
|
||||||
...(!secondValidation.valid ? { validationErrors: secondValidation.errors } : {}),
|
...(!secondValidation.valid ? { validationErrors: secondValidation.errors } : {}),
|
||||||
};
|
};
|
||||||
@@ -1094,6 +1249,7 @@ function executionFromToolDecision(
|
|||||||
return {
|
return {
|
||||||
finalText: "",
|
finalText: "",
|
||||||
chunks: [],
|
chunks: [],
|
||||||
|
reasoningItems: attempt.reasoningItems,
|
||||||
toolCalls: decision.toolCalls,
|
toolCalls: decision.toolCalls,
|
||||||
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
|
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
|
||||||
};
|
};
|
||||||
@@ -1101,6 +1257,7 @@ function executionFromToolDecision(
|
|||||||
return {
|
return {
|
||||||
finalText: decision.content,
|
finalText: decision.content,
|
||||||
chunks: [decision.content],
|
chunks: [decision.content],
|
||||||
|
reasoningItems: attempt.reasoningItems,
|
||||||
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
|
...(attempt.result.usage ? { usage: attempt.result.usage } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1134,23 +1291,32 @@ async function executeAttempt(
|
|||||||
writer?: ResponsesSseWriter,
|
writer?: ResponsesSseWriter,
|
||||||
): Promise<AttemptResult> {
|
): Promise<AttemptResult> {
|
||||||
const accumulator = new FinalTurnAccumulator();
|
const accumulator = new FinalTurnAccumulator();
|
||||||
const result = await runCommandCode(
|
const reasoning = new ResponseReasoningAccumulator(writer);
|
||||||
options.config,
|
let result: CommandResult;
|
||||||
options.cliModel,
|
try {
|
||||||
options.effort,
|
result = await runCommandCode(
|
||||||
prompt,
|
options.config,
|
||||||
options.signal,
|
options.cliModel,
|
||||||
{
|
options.effort,
|
||||||
timeoutMs: options.deadline - Date.now(),
|
prompt,
|
||||||
onEvent: (event) => {
|
options.signal,
|
||||||
const chunks = accumulator.event(event);
|
{
|
||||||
if (chunks && writer) writer.addText(chunks);
|
timeoutMs: options.deadline - Date.now(),
|
||||||
|
onEvent: (event) => {
|
||||||
|
reasoning.event(event);
|
||||||
|
const chunks = accumulator.event(event);
|
||||||
|
if (chunks && writer) writer.addText(chunks);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
);
|
} catch (error) {
|
||||||
|
reasoning.finish();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
result,
|
result,
|
||||||
finalChunks: accumulator.finalChunks ?? [result.finalText],
|
finalChunks: accumulator.finalChunks ?? [result.finalText],
|
||||||
|
reasoningItems: reasoning.finish(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1211,6 +1377,7 @@ function createResponse(options: {
|
|||||||
effort: string;
|
effort: string;
|
||||||
status: ResponseStatus;
|
status: ResponseStatus;
|
||||||
finalText?: string;
|
finalText?: string;
|
||||||
|
reasoningItems?: ResponseOutputReasoning[];
|
||||||
toolCalls?: FunctionToolCall[];
|
toolCalls?: FunctionToolCall[];
|
||||||
usage?: CommandUsage;
|
usage?: CommandUsage;
|
||||||
error?: ResponseError;
|
error?: ResponseError;
|
||||||
@@ -1219,7 +1386,7 @@ function createResponse(options: {
|
|||||||
const finalText = options.finalText ?? "";
|
const finalText = options.finalText ?? "";
|
||||||
const terminal = options.status !== "in_progress";
|
const terminal = options.status !== "in_progress";
|
||||||
const outputStatus = options.status === "completed" ? "completed" : "incomplete";
|
const outputStatus = options.status === "completed" ? "completed" : "incomplete";
|
||||||
const output: ResponseOutputItem[] = options.toolCalls
|
const generatedOutput: ResponseOutputItem[] = options.toolCalls
|
||||||
? options.toolCalls.map((call) => ({
|
? options.toolCalls.map((call) => ({
|
||||||
id: newFunctionCallItemId(),
|
id: newFunctionCallItemId(),
|
||||||
type: "function_call",
|
type: "function_call",
|
||||||
@@ -1237,6 +1404,10 @@ function createResponse(options: {
|
|||||||
content: [{ type: "output_text", text: finalText, annotations: [] }],
|
content: [{ type: "output_text", text: finalText, annotations: [] }],
|
||||||
}]
|
}]
|
||||||
: [];
|
: [];
|
||||||
|
const output: ResponseOutputItem[] = [
|
||||||
|
...(options.reasoningItems ?? []),
|
||||||
|
...generatedOutput,
|
||||||
|
];
|
||||||
return {
|
return {
|
||||||
id: options.id,
|
id: options.id,
|
||||||
object: "response",
|
object: "response",
|
||||||
@@ -1623,6 +1794,10 @@ function newFunctionCallOutputItemId(): string {
|
|||||||
return `fco_local_${randomHexId()}`;
|
return `fco_local_${randomHexId()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function newReasoningItemId(): string {
|
||||||
|
return `rs_local_${randomHexId()}`;
|
||||||
|
}
|
||||||
|
|
||||||
function randomHexId(): string {
|
function randomHexId(): string {
|
||||||
return randomUUID().replaceAll("-", "");
|
return randomUUID().replaceAll("-", "");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user