Files
wehub-resource-sync d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (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 / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

108 lines
3.3 KiB
TypeScript

import { createLogger } from '@sim/logger'
import { toError } from '@sim/utils/errors'
import type { ChatCompletionChunk } from 'openai/resources/chat/completions'
import type { CompletionUsage } from 'openai/resources/completions'
import { checkForForcedToolUsageOpenAI, createOpenAICompatibleStream } from '@/providers/utils'
const logger = createLogger('OpenRouterUtils')
interface OpenRouterModelData {
id: string
supported_parameters?: string[]
}
interface ModelCapabilities {
supportsStructuredOutputs: boolean
supportsTools: boolean
}
let modelCapabilitiesCache: Map<string, ModelCapabilities> | null = null
let cacheTimestamp = 0
const CACHE_TTL_MS = 5 * 60 * 1000 // 5 minutes
async function fetchModelCapabilities(): Promise<Map<string, ModelCapabilities>> {
try {
const response = await fetch('https://openrouter.ai/api/v1/models', {
headers: { 'Content-Type': 'application/json' },
})
if (!response.ok) {
await response.text().catch(() => {})
logger.warn('Failed to fetch OpenRouter model capabilities', {
status: response.status,
})
return new Map()
}
const data = await response.json()
const capabilities = new Map<string, ModelCapabilities>()
for (const model of (data.data ?? []) as OpenRouterModelData[]) {
const supportedParams = model.supported_parameters ?? []
capabilities.set(model.id, {
supportsStructuredOutputs: supportedParams.includes('structured_outputs'),
supportsTools: supportedParams.includes('tools'),
})
}
logger.info('Cached OpenRouter model capabilities', {
modelCount: capabilities.size,
withStructuredOutputs: Array.from(capabilities.values()).filter(
(c) => c.supportsStructuredOutputs
).length,
})
return capabilities
} catch (error) {
logger.error('Error fetching OpenRouter model capabilities', {
error: toError(error).message,
})
return new Map()
}
}
/**
* Gets capabilities for a specific OpenRouter model.
* Fetches from API if cache is stale or empty.
*/
export async function getOpenRouterModelCapabilities(
modelId: string
): Promise<ModelCapabilities | null> {
const now = Date.now()
if (!modelCapabilitiesCache || now - cacheTimestamp > CACHE_TTL_MS) {
modelCapabilitiesCache = await fetchModelCapabilities()
cacheTimestamp = now
}
const normalizedId = modelId.replace(/^openrouter\//, '')
return modelCapabilitiesCache.get(normalizedId) ?? null
}
export async function supportsNativeStructuredOutputs(modelId: string): Promise<boolean> {
const capabilities = await getOpenRouterModelCapabilities(modelId)
return capabilities?.supportsStructuredOutputs ?? false
}
export function createReadableStreamFromOpenAIStream(
openaiStream: AsyncIterable<ChatCompletionChunk>,
onComplete?: (content: string, usage: CompletionUsage) => void
): ReadableStream<Uint8Array> {
return createOpenAICompatibleStream(openaiStream, 'OpenRouter', onComplete)
}
export function checkForForcedToolUsage(
response: any,
toolChoice: string | { type: string; function?: { name: string }; name?: string; any?: any },
forcedTools: string[],
usedForcedTools: string[]
): { hasUsedForcedTool: boolean; usedForcedTools: string[] } {
return checkForForcedToolUsageOpenAI(
response,
toolChoice,
'OpenRouter',
forcedTools,
usedForcedTools
)
}