Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00

160 lines
5.0 KiB
TypeScript

import type { RawMessageStreamEvent } from '@anthropic-ai/sdk/resources'
import { createLogger } from '@sim/logger'
import {
type AnthropicUsageAccumulator,
type AnthropicUsageLike,
addAnthropicUsage,
createAnthropicUsageAccumulator,
} from '@/providers/anthropic/usage'
import type { AgentStreamEvent } from '@/providers/stream-events'
import { trackForcedToolUsage } from '@/providers/utils'
const logger = createLogger('AnthropicUtils')
export interface AnthropicStreamComplete {
content: string
usage: AnthropicUsageAccumulator
/** Assembled thinking text for traces (redacted blocks become `[redacted]`). */
thinking: string
}
/**
* Converts an Anthropic Messages stream into an in-process
* {@link AgentStreamEvent} object stream (`thinking_delta` + `text_delta`).
* Tool_use / input_json deltas are ignored here — use
* {@link createAnthropicStreamingToolLoopStream} for the live tool loop.
*/
export function createReadableStreamFromAnthropicStream(
anthropicStream: AsyncIterable<RawMessageStreamEvent>,
onComplete?: (result: AnthropicStreamComplete) => void
): ReadableStream<AgentStreamEvent> {
let cancelled = false
let streamIterator: AsyncIterator<RawMessageStreamEvent> | undefined
return new ReadableStream<AgentStreamEvent>({
async start(controller) {
try {
let fullContent = ''
const thinkingBlocks: string[] = []
let currentThinking = ''
let usageSnapshot: AnthropicUsageLike = {}
const flushThinkingBlock = () => {
if (currentThinking) {
thinkingBlocks.push(currentThinking)
currentThinking = ''
}
}
streamIterator = anthropicStream[Symbol.asyncIterator]()
while (true) {
const next = await streamIterator.next()
if (next.done || cancelled) break
const event = next.value
if (event.type === 'message_start') {
usageSnapshot = event.message.usage
continue
}
if (event.type === 'message_delta') {
usageSnapshot = {
...usageSnapshot,
input_tokens: event.usage.input_tokens ?? usageSnapshot.input_tokens,
output_tokens: event.usage.output_tokens ?? usageSnapshot.output_tokens,
cache_read_input_tokens:
event.usage.cache_read_input_tokens ?? usageSnapshot.cache_read_input_tokens,
cache_creation_input_tokens:
event.usage.cache_creation_input_tokens ??
usageSnapshot.cache_creation_input_tokens,
}
continue
}
if (event.type === 'content_block_start') {
if (event.content_block.type === 'redacted_thinking') {
flushThinkingBlock()
thinkingBlocks.push('[redacted]')
} else if (event.content_block.type === 'thinking') {
flushThinkingBlock()
}
continue
}
if (event.type === 'content_block_stop') {
flushThinkingBlock()
continue
}
if (event.type !== 'content_block_delta') {
continue
}
const delta = event.delta
if (delta.type === 'thinking_delta' && typeof delta.thinking === 'string') {
currentThinking += delta.thinking
controller.enqueue({ type: 'thinking_delta', text: delta.thinking })
continue
}
if (delta.type === 'text_delta' && typeof delta.text === 'string') {
flushThinkingBlock()
fullContent += delta.text
controller.enqueue({ type: 'text_delta', text: delta.text, turn: 'final' })
}
}
if (cancelled) return
flushThinkingBlock()
if (onComplete) {
const usage = createAnthropicUsageAccumulator()
addAnthropicUsage(usage, usageSnapshot)
onComplete({
content: fullContent,
usage,
thinking: thinkingBlocks.filter(Boolean).join('\n\n'),
})
}
controller.close()
} catch (err) {
if (!cancelled) {
controller.error(err)
}
}
},
async cancel() {
cancelled = true
await streamIterator?.return?.()
},
})
}
export function checkForForcedToolUsage(
response: any,
toolChoice: any,
forcedTools: string[],
usedForcedTools: string[]
): { hasUsedForcedTool: boolean; usedForcedTools: string[] } | null {
if (typeof toolChoice === 'object' && toolChoice !== null && Array.isArray(response.content)) {
const toolUses = response.content.filter((item: any) => item.type === 'tool_use')
if (toolUses.length > 0) {
const adaptedToolCalls = toolUses.map((tool: any) => ({ name: tool.name }))
const adaptedToolChoice =
toolChoice.type === 'tool' ? { function: { name: toolChoice.name } } : toolChoice
return trackForcedToolUsage(
adaptedToolCalls,
adaptedToolChoice,
logger,
'anthropic',
forcedTools,
usedForcedTools
)
}
}
return null
}