mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-22 08:15:53 +00:00
* feat: langfuse tracing 增加 thinking 参数记录 在 recordLLMObservation 中添加 thinking 配置(type/budgetTokens), 所有 provider(claude/gemini/openai)及 tokenEstimation、sideQuery 调用处同步传递 thinking 信息,便于 Langfuse 面板观察 thinking 使用情况。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: langfuse tracing 兼容 budget_tokens snake_case 格式 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: 统一传递完整 thinking 配置而非仅 thinkingType Langfuse 追踪直接传递整个 thinking 对象(含 type 和 budget_tokens), Analytics 日志同步补充 thinkingBudgetTokens 字段,logAPIQuery 改为 接收 ThinkingConfig 类型参数。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat: 省略旧消息的代码 diff 展示,仅保留最新消息的完整 diff * fix: Edit 工具增加 Tab/空格规范化匹配,修复中文和缩进文件编辑失败 Read 工具输出将 Tab 渲染为空格,用户复制后 Edit 工具无法匹配。 在 findActualString 中增加 Tab→空格规范化回退匹配,并精确映射回原始文件位置。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * docs: README 添加安装/更新失败的解决方案提示 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1776,6 +1776,10 @@ async function* queryModel(
|
||||
// captures only primitives instead of paramsFromContext's full closure scope
|
||||
// (messagesForAPI, system, allTools, betas — the entire request-building
|
||||
// context), which would otherwise be pinned until the promise resolves.
|
||||
// Also capture thinking params for Langfuse observability.
|
||||
// Pass the entire thinking config object so all fields (type, budget_tokens,
|
||||
// and any future additions) flow through without cherry-picking.
|
||||
let langfuseThinking: BetaMessageStreamParams['thinking'] | undefined
|
||||
{
|
||||
const queryParams = paramsFromContext({
|
||||
model: options.model,
|
||||
@@ -1783,8 +1787,10 @@ async function* queryModel(
|
||||
})
|
||||
const logMessagesLength = queryParams.messages.length
|
||||
const logBetas = useBetas ? (queryParams.betas ?? []) : []
|
||||
const logThinkingType = queryParams.thinking?.type ?? 'disabled'
|
||||
const logEffortValue = queryParams.output_config?.effort
|
||||
if (queryParams.thinking && queryParams.thinking.type !== 'disabled') {
|
||||
langfuseThinking = queryParams.thinking
|
||||
}
|
||||
void options.getToolPermissionContext().then(permissionContext => {
|
||||
logAPIQuery({
|
||||
model: options.model,
|
||||
@@ -1794,7 +1800,7 @@ async function* queryModel(
|
||||
permissionMode: permissionContext.mode,
|
||||
querySource: options.querySource,
|
||||
queryTracking: options.queryTracking,
|
||||
thinkingType: logThinkingType,
|
||||
thinkingConfig,
|
||||
effortValue: logEffortValue,
|
||||
fastMode: isFastMode,
|
||||
previousRequestId,
|
||||
@@ -2545,6 +2551,9 @@ async function* queryModel(
|
||||
maxOutputTokens,
|
||||
thinkingType:
|
||||
thinkingConfig.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
...(thinkingConfig.type === 'enabled' && {
|
||||
thinkingBudgetTokens: thinkingConfig.budgetTokens,
|
||||
}),
|
||||
fallback_disabled: true,
|
||||
request_id: (streamRequestId ??
|
||||
'unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
@@ -2577,6 +2586,9 @@ async function* queryModel(
|
||||
maxOutputTokens,
|
||||
thinkingType:
|
||||
thinkingConfig.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
...(thinkingConfig.type === 'enabled' && {
|
||||
thinkingBudgetTokens: thinkingConfig.budgetTokens,
|
||||
}),
|
||||
fallback_disabled: false,
|
||||
request_id: (streamRequestId ??
|
||||
'unknown') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
@@ -2693,6 +2705,9 @@ async function* queryModel(
|
||||
maxOutputTokens,
|
||||
thinkingType:
|
||||
thinkingConfig.type as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
...(thinkingConfig.type === 'enabled' && {
|
||||
thinkingBudgetTokens: thinkingConfig.budgetTokens,
|
||||
}),
|
||||
request_id:
|
||||
failedRequestId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
fallback_cause:
|
||||
@@ -2925,6 +2940,7 @@ async function* queryModel(
|
||||
endTime: new Date(),
|
||||
completionStartTime: ttftMs > 0 ? new Date(start + ttftMs) : undefined,
|
||||
tools: convertToolsToLangfuse(toolSchemas as unknown[]),
|
||||
thinking: langfuseThinking,
|
||||
})
|
||||
|
||||
void options.getToolPermissionContext().then(permissionContext => {
|
||||
|
||||
@@ -193,6 +193,15 @@ export async function* queryModelGemini(
|
||||
endTime: new Date(),
|
||||
completionStartTime: ttftMs > 0 ? new Date(start + ttftMs) : undefined,
|
||||
tools: convertToolsToLangfuse(toolSchemas as unknown[]),
|
||||
thinking:
|
||||
thinkingConfig.type !== 'disabled'
|
||||
? {
|
||||
type: thinkingConfig.type,
|
||||
...(thinkingConfig.type === 'enabled' && {
|
||||
budgetTokens: thinkingConfig.budgetTokens,
|
||||
}),
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error)
|
||||
|
||||
@@ -23,6 +23,7 @@ import { getAPIProviderForStatsig } from 'src/utils/model/providers.js'
|
||||
import type { PermissionMode } from 'src/utils/permissions/PermissionMode.js'
|
||||
import { jsonStringify } from 'src/utils/slowOperations.js'
|
||||
import { logOTelEvent } from 'src/utils/telemetry/events.js'
|
||||
import type { ThinkingConfig } from 'src/utils/thinking.js'
|
||||
import {
|
||||
endLLMRequestSpan,
|
||||
isBetaTracingEnabled,
|
||||
@@ -176,7 +177,7 @@ export function logAPIQuery({
|
||||
permissionMode,
|
||||
querySource,
|
||||
queryTracking,
|
||||
thinkingType,
|
||||
thinkingConfig,
|
||||
effortValue,
|
||||
fastMode,
|
||||
previousRequestId,
|
||||
@@ -188,11 +189,13 @@ export function logAPIQuery({
|
||||
permissionMode?: PermissionMode
|
||||
querySource: string
|
||||
queryTracking?: QueryChainTracking
|
||||
thinkingType?: 'adaptive' | 'enabled' | 'disabled'
|
||||
thinkingConfig?: ThinkingConfig
|
||||
effortValue?: EffortLevel | null
|
||||
fastMode?: boolean
|
||||
previousRequestId?: string | null
|
||||
}): void {
|
||||
const thinkingType = thinkingConfig?.type ?? 'disabled'
|
||||
const thinkingBudgetTokens = thinkingConfig?.type === 'enabled' ? thinkingConfig.budgetTokens : undefined
|
||||
logEvent('tengu_api_query', {
|
||||
model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
messagesLength,
|
||||
@@ -219,6 +222,9 @@ export function logAPIQuery({
|
||||
: {}),
|
||||
thinkingType:
|
||||
thinkingType as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
...(thinkingBudgetTokens !== undefined && {
|
||||
thinkingBudgetTokens,
|
||||
}),
|
||||
effortValue:
|
||||
effortValue as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
fastMode,
|
||||
|
||||
@@ -418,6 +418,7 @@ export async function* queryModelOpenAI(
|
||||
endTime: new Date(),
|
||||
completionStartTime: ttftMs > 0 ? new Date(start + ttftMs) : undefined,
|
||||
tools: convertToolsToLangfuse(toolSchemas as unknown[]),
|
||||
...(enableThinking && { thinking: { type: 'enabled' } }),
|
||||
})
|
||||
|
||||
// Safety: if stream ended without message_stop, assemble and yield whatever we have
|
||||
|
||||
Reference in New Issue
Block a user