mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-09 22:25:02 +03:00
feat(core): report Claude Agent plan usage on step metadata (#60879)
This commit is contained in:
committed by
GitHub
Unverified
parent
4f3156ab3c
commit
c8a473bead
+7
-1
@@ -1,7 +1,7 @@
|
||||
export * as ConvertClaudeStream from "./convert-claude-stream.js"
|
||||
|
||||
import type { LanguageModelV3FunctionTool, LanguageModelV3StreamPart } from "@ai-sdk/provider"
|
||||
import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk"
|
||||
import type { SDKMessage, SDKRateLimitInfo } from "@anthropic-ai/claude-agent-sdk"
|
||||
import { Provider } from "@opencode/schema/provider"
|
||||
import { ClaudeUsage } from "./usage.js"
|
||||
import { ToolNames } from "../tools/names.js"
|
||||
@@ -15,11 +15,15 @@ export function make(tools: readonly LanguageModelV3FunctionTool[]) {
|
||||
let messageID = ""
|
||||
let calledTools = false
|
||||
let metadata: Record<string, { sessionID: string; messageID: string }> | undefined
|
||||
// Plugins read plan usage from step metadata instead of polling Anthropic for it.
|
||||
let rateLimit: SDKRateLimitInfo | undefined
|
||||
const finishMetadata = () => (rateLimit ? { [Provider.ID.claudeAgent]: { rateLimit } } : undefined)
|
||||
|
||||
return (message: SDKMessage): LanguageModelV3StreamPart[] => {
|
||||
const parts: LanguageModelV3StreamPart[] = []
|
||||
const error = failure(message)
|
||||
if (error) return [{ type: "error", error }]
|
||||
if (message.type === "rate_limit_event") rateLimit = message.rate_limit_info
|
||||
if (message.type === "stream_event" && message.parent_tool_use_id === null) {
|
||||
const event = message.event
|
||||
switch (event.type) {
|
||||
@@ -74,6 +78,7 @@ export function make(tools: readonly LanguageModelV3FunctionTool[]) {
|
||||
type: "finish",
|
||||
finishReason: { unified: "tool-calls", raw: "tool_use" },
|
||||
usage: usage.snapshot(),
|
||||
providerMetadata: finishMetadata(),
|
||||
})
|
||||
}
|
||||
break
|
||||
@@ -126,6 +131,7 @@ export function make(tools: readonly LanguageModelV3FunctionTool[]) {
|
||||
raw: message.stop_reason ?? undefined,
|
||||
},
|
||||
usage: usage.snapshot(),
|
||||
providerMetadata: finishMetadata(),
|
||||
})
|
||||
}
|
||||
return parts
|
||||
|
||||
@@ -28,6 +28,22 @@ describe("Claude conversions", () => {
|
||||
expect(unknown.some((part) => part.type === "tool-call" || part.type === "finish")).toBe(false)
|
||||
})
|
||||
|
||||
test("a step's finish carries the plan usage Claude Code reported during it", () => {
|
||||
const recorded = Recorder.load("tools").find(
|
||||
(line) => line.from === "claude" && line.message.type === "rate_limit_event",
|
||||
)
|
||||
if (recorded?.from !== "claude" || recorded.message.type !== "rate_limit_event")
|
||||
throw new Error("Expected a recorded rate limit event")
|
||||
expect(firstToolReply(TestTools.tools).at(-1)).toMatchObject({
|
||||
type: "finish",
|
||||
providerMetadata: { "claude-agent": { rateLimit: recorded.message.rate_limit_info } },
|
||||
})
|
||||
expect(ConvertClaudeStream.make([])(Recorder.result("text")).at(-1)).toMatchObject({
|
||||
type: "finish",
|
||||
providerMetadata: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
test("utility calls keep system instructions separate from conversation content and omit thinking", () => {
|
||||
const prompt: LanguageModelV3Prompt = [
|
||||
{ role: "system", content: "Summarize this conversation." },
|
||||
|
||||
Reference in New Issue
Block a user