sync
parent
8b1c55f9fa
commit
2dbb029472
|
|
@ -456,9 +456,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
<img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-6 p-0.5 shrink-0" />
|
||||
<div class="flex gap-x-3 items-baseline flex-[1_0_0]">
|
||||
<span class="text-14-medium text-text-strong overflow-hidden text-ellipsis">{i.name}</span>
|
||||
<Show when={i.release_date}>
|
||||
<Show when={false}>
|
||||
<span class="text-12-medium text-text-weak overflow-hidden text-ellipsis truncate min-w-0">
|
||||
{DateTime.fromFormat(i.release_date, "yyyy-MM-dd").toFormat("LLL yyyy")}
|
||||
{DateTime.fromFormat("unknown", "yyyy-MM-dd").toFormat("LLL yyyy")}
|
||||
</span>
|
||||
</Show>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ export namespace Agent {
|
|||
export async function generate(input: { description: string }) {
|
||||
const defaultModel = await Provider.defaultModel()
|
||||
const model = await Provider.getModel(defaultModel.providerID, defaultModel.modelID)
|
||||
const language = await Provider.getLanguage(model)
|
||||
const system = SystemPrompt.header(defaultModel.providerID)
|
||||
system.push(PROMPT_GENERATE)
|
||||
const existing = await list()
|
||||
|
|
@ -241,7 +242,7 @@ export namespace Agent {
|
|||
content: `Create an agent configuration based on this request: \"${input.description}\".\n\nIMPORTANT: The following identifiers already exist and must NOT be used: ${existing.map((i) => i.name).join(", ")}\n Return ONLY the JSON object, no other text, do not wrap in backticks`,
|
||||
},
|
||||
],
|
||||
model: model.language,
|
||||
model: language,
|
||||
schema: z.object({
|
||||
identifier: z.string(),
|
||||
whenToUse: z.string(),
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ export const ModelsCommand = cmd({
|
|||
|
||||
function printModels(providerID: string, verbose?: boolean) {
|
||||
const provider = providers[providerID]
|
||||
const sortedModels = Object.entries(provider.info.models).sort(([a], [b]) => a.localeCompare(b))
|
||||
const sortedModels = Object.entries(provider.models).sort(([a], [b]) => a.localeCompare(b))
|
||||
for (const [modelID, model] of sortedModels) {
|
||||
process.stdout.write(`${providerID}/${modelID}`)
|
||||
process.stdout.write(EOL)
|
||||
|
|
|
|||
|
|
@ -9,63 +9,55 @@ export namespace ModelsDev {
|
|||
const log = Log.create({ service: "models.dev" })
|
||||
const filepath = path.join(Global.Path.cache, "models.json")
|
||||
|
||||
export const Model = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
target: z.string(),
|
||||
release_date: z.string(),
|
||||
attachment: z.boolean(),
|
||||
reasoning: z.boolean(),
|
||||
temperature: z.boolean(),
|
||||
tool_call: z.boolean(),
|
||||
cost: z.object({
|
||||
input: z.number(),
|
||||
output: z.number(),
|
||||
cache_read: z.number().optional(),
|
||||
cache_write: z.number().optional(),
|
||||
context_over_200k: z
|
||||
.object({
|
||||
input: z.number(),
|
||||
output: z.number(),
|
||||
cache_read: z.number().optional(),
|
||||
cache_write: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
limit: z.object({
|
||||
context: z.number(),
|
||||
output: z.number(),
|
||||
}),
|
||||
modalities: z
|
||||
export const Model = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
target: z.string(),
|
||||
release_date: z.string(),
|
||||
attachment: z.boolean(),
|
||||
reasoning: z.boolean(),
|
||||
temperature: z.boolean(),
|
||||
tool_call: z.boolean(),
|
||||
cost: z.object({
|
||||
input: z.number(),
|
||||
output: z.number(),
|
||||
cache_read: z.number().optional(),
|
||||
cache_write: z.number().optional(),
|
||||
context_over_200k: z
|
||||
.object({
|
||||
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
||||
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
||||
input: z.number(),
|
||||
output: z.number(),
|
||||
cache_read: z.number().optional(),
|
||||
cache_write: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
experimental: z.boolean().optional(),
|
||||
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
||||
options: z.record(z.string(), z.any()),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
provider: z.object({ npm: z.string() }).optional(),
|
||||
})
|
||||
.meta({
|
||||
ref: "Model",
|
||||
})
|
||||
}),
|
||||
limit: z.object({
|
||||
context: z.number(),
|
||||
output: z.number(),
|
||||
}),
|
||||
modalities: z
|
||||
.object({
|
||||
input: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
||||
output: z.array(z.enum(["text", "audio", "image", "video", "pdf"])),
|
||||
})
|
||||
.optional(),
|
||||
experimental: z.boolean().optional(),
|
||||
status: z.enum(["alpha", "beta", "deprecated"]).optional(),
|
||||
options: z.record(z.string(), z.any()),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
provider: z.object({ npm: z.string() }).optional(),
|
||||
})
|
||||
export type Model = z.infer<typeof Model>
|
||||
|
||||
export const Provider = z
|
||||
.object({
|
||||
api: z.string().optional(),
|
||||
name: z.string(),
|
||||
env: z.array(z.string()),
|
||||
id: z.string(),
|
||||
npm: z.string().optional(),
|
||||
models: z.record(z.string(), Model),
|
||||
})
|
||||
.meta({
|
||||
ref: "Provider",
|
||||
})
|
||||
export const Provider = z.object({
|
||||
api: z.string().optional(),
|
||||
name: z.string(),
|
||||
env: z.array(z.string()),
|
||||
id: z.string(),
|
||||
npm: z.string().optional(),
|
||||
models: z.record(z.string(), Model),
|
||||
})
|
||||
|
||||
export type Provider = z.infer<typeof Provider>
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { createVertex } from "@ai-sdk/google-vertex"
|
|||
import { createVertexAnthropic } from "@ai-sdk/google-vertex/anthropic"
|
||||
import { createOpenAI } from "@ai-sdk/openai"
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
|
||||
import { createOpenRouter } from "@openrouter/ai-sdk-provider"
|
||||
import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider"
|
||||
import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src"
|
||||
|
||||
export namespace Provider {
|
||||
|
|
@ -43,14 +43,13 @@ export namespace Provider {
|
|||
"@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,
|
||||
}
|
||||
|
||||
type CustomModelLoader = (sdk: any, modelID: string, options?: Record<string, any>) => Promise<any>
|
||||
type CustomLoader = (provider: Info) => Promise<{
|
||||
autoload: boolean
|
||||
getModel?: (sdk: any, modelID: string, options?: Record<string, any>) => Promise<any>
|
||||
getModel?: CustomModelLoader
|
||||
options?: Record<string, any>
|
||||
}>
|
||||
|
||||
type Source = "env" | "config" | "custom" | "api"
|
||||
|
||||
const CUSTOM_LOADERS: Record<string, CustomLoader> = {
|
||||
async anthropic() {
|
||||
return {
|
||||
|
|
@ -314,20 +313,20 @@ export namespace Provider {
|
|||
reasoning: z.boolean(),
|
||||
attachment: z.boolean(),
|
||||
toolcall: z.boolean(),
|
||||
input: {
|
||||
input: z.object({
|
||||
text: z.boolean(),
|
||||
audio: z.boolean(),
|
||||
image: z.boolean(),
|
||||
video: z.boolean(),
|
||||
pdf: z.boolean(),
|
||||
},
|
||||
output: {
|
||||
}),
|
||||
output: z.object({
|
||||
text: z.boolean(),
|
||||
audio: z.boolean(),
|
||||
image: z.boolean(),
|
||||
video: z.boolean(),
|
||||
pdf: z.boolean(),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
cost: z.object({
|
||||
input: z.number(),
|
||||
|
|
@ -433,7 +432,7 @@ export namespace Provider {
|
|||
}
|
||||
}
|
||||
|
||||
function fromModelsDevProvider(provider: ModelsDev.Provider): Info {
|
||||
export function fromModelsDevProvider(provider: ModelsDev.Provider): Info {
|
||||
return {
|
||||
id: provider.id,
|
||||
source: "custom",
|
||||
|
|
@ -444,11 +443,6 @@ export namespace Provider {
|
|||
}
|
||||
}
|
||||
|
||||
export type ModelWithStuff = {
|
||||
language: LanguageModel
|
||||
info: Model
|
||||
}
|
||||
|
||||
const state = Instance.state(async () => {
|
||||
using _ = log.time("state")
|
||||
const config = await Config.get()
|
||||
|
|
@ -464,7 +458,10 @@ export namespace Provider {
|
|||
}
|
||||
|
||||
const providers: { [providerID: string]: Info } = {}
|
||||
const models = new Map<string, ModelWithStuff>()
|
||||
const languages = new Map<string, LanguageModelV2>()
|
||||
const modelLoaders: {
|
||||
[providerID: string]: CustomModelLoader
|
||||
} = {}
|
||||
const sdk = new Map<number, SDK>()
|
||||
|
||||
log.info("init")
|
||||
|
|
@ -631,6 +628,7 @@ export namespace Provider {
|
|||
if (disabled.has(providerID)) continue
|
||||
const result = await fn(database[providerID])
|
||||
if (result && (result.autoload || providers[providerID])) {
|
||||
if (result.getModel) modelLoaders[providerID] = result.getModel
|
||||
mergeProvider(providerID, {
|
||||
source: "custom",
|
||||
options: result.options,
|
||||
|
|
@ -645,7 +643,6 @@ export namespace Provider {
|
|||
env: provider.env,
|
||||
name: provider.name,
|
||||
options: provider.options,
|
||||
// TODO: merge models
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -689,9 +686,10 @@ export namespace Provider {
|
|||
}
|
||||
|
||||
return {
|
||||
models,
|
||||
models: languages,
|
||||
providers,
|
||||
sdk,
|
||||
modelLoaders,
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -789,15 +787,7 @@ export namespace Provider {
|
|||
}
|
||||
|
||||
export async function getModel(providerID: string, modelID: string) {
|
||||
const key = `${providerID}/${modelID}`
|
||||
const s = await state()
|
||||
if (s.models.has(key)) return s.models.get(key)!
|
||||
|
||||
log.info("getModel", {
|
||||
providerID,
|
||||
modelID,
|
||||
})
|
||||
|
||||
const provider = s.providers[providerID]
|
||||
if (!provider) {
|
||||
const availableProviders = Object.keys(s.providers)
|
||||
|
|
@ -813,38 +803,29 @@ export namespace Provider {
|
|||
const suggestions = matches.map((m) => m.target)
|
||||
throw new ModelNotFoundError({ providerID, modelID, suggestions })
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
const sdk = await getSDK(info)
|
||||
export async function getLanguage(model: Model) {
|
||||
const s = await state()
|
||||
const key = `${model.providerID}/${model.id}`
|
||||
if (s.models.has(key)) return s.models.get(key)!
|
||||
|
||||
const provider = s.providers[model.providerID]
|
||||
const sdk = await getSDK(model)
|
||||
|
||||
try {
|
||||
const language = provider.getModel
|
||||
? await provider.getModel(sdk, info.api.id, provider.options)
|
||||
: sdk.languageModel(info.api.id)
|
||||
log.info("found", { providerID, modelID })
|
||||
const cached: ModelWithStuff = {
|
||||
info,
|
||||
language,
|
||||
}
|
||||
s.models.set(key, {
|
||||
providerID,
|
||||
modelID,
|
||||
info,
|
||||
language,
|
||||
npm,
|
||||
})
|
||||
return {
|
||||
modelID,
|
||||
providerID,
|
||||
info,
|
||||
language,
|
||||
npm,
|
||||
}
|
||||
const language = s.modelLoaders[model.providerID]
|
||||
? await s.modelLoaders[model.providerID](sdk, model.api.id, provider.options)
|
||||
: sdk.languageModel(model.api.id)
|
||||
s.models.set(key, language)
|
||||
return language
|
||||
} catch (e) {
|
||||
if (e instanceof NoSuchModelError)
|
||||
throw new ModelNotFoundError(
|
||||
{
|
||||
modelID: modelID,
|
||||
providerID,
|
||||
modelID: model.id,
|
||||
providerID: model.providerID,
|
||||
},
|
||||
{ cause: e },
|
||||
)
|
||||
|
|
@ -857,7 +838,7 @@ export namespace Provider {
|
|||
const provider = s.providers[providerID]
|
||||
if (!provider) return undefined
|
||||
for (const item of query) {
|
||||
for (const modelID of Object.keys(provider.info.models)) {
|
||||
for (const modelID of Object.keys(provider.models)) {
|
||||
if (modelID.includes(item))
|
||||
return {
|
||||
providerID,
|
||||
|
|
@ -893,7 +874,7 @@ export namespace Provider {
|
|||
priority = ["gpt-5-nano"]
|
||||
}
|
||||
for (const item of priority) {
|
||||
for (const model of Object.keys(provider.info.models)) {
|
||||
for (const model of Object.keys(provider.models)) {
|
||||
if (model.includes(item)) return getModel(providerID, model)
|
||||
}
|
||||
}
|
||||
|
|
@ -901,7 +882,7 @@ export namespace Provider {
|
|||
|
||||
// Check if opencode provider is available before using it
|
||||
const opencodeProvider = await state().then((state) => state.providers["opencode"])
|
||||
if (opencodeProvider && opencodeProvider.info.models["gpt-5-nano"]) {
|
||||
if (opencodeProvider && opencodeProvider.models["gpt-5-nano"]) {
|
||||
return getModel("opencode", "gpt-5-nano")
|
||||
}
|
||||
|
||||
|
|
@ -924,12 +905,12 @@ export namespace Provider {
|
|||
|
||||
const provider = await list()
|
||||
.then((val) => Object.values(val))
|
||||
.then((x) => x.find((p) => !cfg.provider || Object.keys(cfg.provider).includes(p.info.id)))
|
||||
.then((x) => x.find((p) => !cfg.provider || Object.keys(cfg.provider).includes(p.id)))
|
||||
if (!provider) throw new Error("no providers found")
|
||||
const [model] = sort(Object.values(provider.info.models))
|
||||
const [model] = sort(Object.values(provider.models))
|
||||
if (!model) throw new Error("no models found")
|
||||
return {
|
||||
providerID: provider.info.id,
|
||||
providerID: provider.id,
|
||||
modelID: model.id,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { APICallError, ModelMessage } from "ai"
|
||||
import { unique } from "remeda"
|
||||
import type { JSONSchema } from "zod/v4/core"
|
||||
import type { ModelsDev } from "./models"
|
||||
import type { Provider } from "./provider"
|
||||
|
||||
export namespace ProviderTransform {
|
||||
function normalizeMessages(msgs: ModelMessage[], providerID: string, model: ModelsDev.Model): ModelMessage[] {
|
||||
if (model.target.includes("claude")) {
|
||||
function normalizeMessages(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
|
||||
if (model.api.id.includes("claude")) {
|
||||
return msgs.map((msg) => {
|
||||
if ((msg.role === "assistant" || msg.role === "tool") && Array.isArray(msg.content)) {
|
||||
msg.content = msg.content.map((part) => {
|
||||
|
|
@ -21,7 +21,7 @@ export namespace ProviderTransform {
|
|||
return msg
|
||||
})
|
||||
}
|
||||
if (providerID === "mistral" || model.target.toLowerCase().includes("mistral")) {
|
||||
if (model.providerID === "mistral" || model.api.id.toLowerCase().includes("mistral")) {
|
||||
const result: ModelMessage[] = []
|
||||
for (let i = 0; i < msgs.length; i++) {
|
||||
const msg = msgs[i]
|
||||
|
|
@ -108,67 +108,68 @@ export namespace ProviderTransform {
|
|||
return msgs
|
||||
}
|
||||
|
||||
export function message(msgs: ModelMessage[], providerID: string, model: ModelsDev.Model) {
|
||||
msgs = normalizeMessages(msgs, providerID, model)
|
||||
if (providerID === "anthropic" || model.target.includes("anthropic") || model.target.includes("claude")) {
|
||||
msgs = applyCaching(msgs, providerID)
|
||||
export function message(msgs: ModelMessage[], model: Provider.Model) {
|
||||
msgs = normalizeMessages(msgs, model)
|
||||
if (model.providerID === "anthropic" || model.api.id.includes("anthropic") || model.api.id.includes("claude")) {
|
||||
msgs = applyCaching(msgs, model.providerID)
|
||||
}
|
||||
|
||||
return msgs
|
||||
}
|
||||
|
||||
export function temperature(model: ModelsDev.Model) {
|
||||
if (model.target.toLowerCase().includes("qwen")) return 0.55
|
||||
if (model.target.toLowerCase().includes("claude")) return undefined
|
||||
if (model.target.toLowerCase().includes("gemini-3-pro")) return 1.0
|
||||
export function temperature(model: Provider.Model) {
|
||||
if (model.api.id.toLowerCase().includes("qwen")) return 0.55
|
||||
if (model.api.id.toLowerCase().includes("claude")) return undefined
|
||||
if (model.api.id.toLowerCase().includes("gemini-3-pro")) return 1.0
|
||||
return 0
|
||||
}
|
||||
|
||||
export function topP(model: ModelsDev.Model) {
|
||||
if (model.target.toLowerCase().includes("qwen")) return 1
|
||||
export function topP(model: Provider.Model) {
|
||||
if (model.api.id.toLowerCase().includes("qwen")) return 1
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function options(
|
||||
providerID: string,
|
||||
model: ModelsDev.Model,
|
||||
npm: string,
|
||||
model: Provider.Model,
|
||||
sessionID: string,
|
||||
providerOptions?: Record<string, any>,
|
||||
): Record<string, any> {
|
||||
const result: Record<string, any> = {}
|
||||
|
||||
// switch to providerID later, for now use this
|
||||
if (npm === "@openrouter/ai-sdk-provider") {
|
||||
if (model.api.npm === "@openrouter/ai-sdk-provider") {
|
||||
result["usage"] = {
|
||||
include: true,
|
||||
}
|
||||
}
|
||||
|
||||
if (providerID === "openai" || providerOptions?.setCacheKey) {
|
||||
if (model.providerID === "openai" || providerOptions?.setCacheKey) {
|
||||
result["promptCacheKey"] = sessionID
|
||||
}
|
||||
|
||||
if (providerID === "google" || (providerID.startsWith("opencode") && model.target.includes("gemini-3"))) {
|
||||
if (
|
||||
model.providerID === "google" ||
|
||||
(model.providerID.startsWith("opencode") && model.api.id.includes("gemini-3"))
|
||||
) {
|
||||
result["thinkingConfig"] = {
|
||||
includeThoughts: true,
|
||||
}
|
||||
}
|
||||
|
||||
if (model.target.includes("gpt-5") && !model.target.includes("gpt-5-chat")) {
|
||||
if (model.target.includes("codex")) {
|
||||
if (model.providerID.includes("gpt-5") && !model.api.id.includes("gpt-5-chat")) {
|
||||
if (model.providerID.includes("codex")) {
|
||||
result["store"] = false
|
||||
}
|
||||
|
||||
if (!model.target.includes("codex") && !model.target.includes("gpt-5-pro")) {
|
||||
if (!model.api.id.includes("codex") && !model.api.id.includes("gpt-5-pro")) {
|
||||
result["reasoningEffort"] = "medium"
|
||||
}
|
||||
|
||||
if (model.target.endsWith("gpt-5.1") && providerID !== "azure") {
|
||||
if (model.api.id.endsWith("gpt-5.1") && model.providerID !== "azure") {
|
||||
result["textVerbosity"] = "low"
|
||||
}
|
||||
|
||||
if (providerID.startsWith("opencode")) {
|
||||
if (model.providerID.startsWith("opencode")) {
|
||||
result["promptCacheKey"] = sessionID
|
||||
result["include"] = ["reasoning.encrypted_content"]
|
||||
result["reasoningSummary"] = "auto"
|
||||
|
|
@ -177,17 +178,17 @@ export namespace ProviderTransform {
|
|||
return result
|
||||
}
|
||||
|
||||
export function smallOptions(input: { providerID: string; model: ModelsDev.Model }) {
|
||||
export function smallOptions(model: Provider.Model) {
|
||||
const options: Record<string, any> = {}
|
||||
|
||||
if (input.providerID === "openai" || input.model.target.includes("gpt-5")) {
|
||||
if (input.model.target.includes("5.1")) {
|
||||
if (model.providerID === "openai" || model.api.id.includes("gpt-5")) {
|
||||
if (model.api.id.includes("5.1")) {
|
||||
options["reasoningEffort"] = "low"
|
||||
} else {
|
||||
options["reasoningEffort"] = "minimal"
|
||||
}
|
||||
}
|
||||
if (input.providerID === "google") {
|
||||
if (model.providerID === "google") {
|
||||
options["thinkingConfig"] = {
|
||||
thinkingBudget: 0,
|
||||
}
|
||||
|
|
@ -255,7 +256,7 @@ export namespace ProviderTransform {
|
|||
return standardLimit
|
||||
}
|
||||
|
||||
export function schema(providerID: string, model: ModelsDev.Model, schema: JSONSchema.BaseSchema) {
|
||||
export function schema(model: Provider.Model, schema: JSONSchema.BaseSchema) {
|
||||
/*
|
||||
if (["openai", "azure"].includes(providerID)) {
|
||||
if (schema.type === "object" && schema.properties) {
|
||||
|
|
@ -275,7 +276,7 @@ export namespace ProviderTransform {
|
|||
*/
|
||||
|
||||
// Convert integer enums to string enums for Google/Gemini
|
||||
if (providerID === "google" || model.target.includes("gemini")) {
|
||||
if (model.providerID === "google" || model.api.id.includes("gemini")) {
|
||||
const sanitizeGemini = (obj: any): any => {
|
||||
if (obj === null || typeof obj !== "object") {
|
||||
return obj
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { proxy } from "hono/proxy"
|
|||
import { Session } from "../session"
|
||||
import z from "zod"
|
||||
import { Provider } from "../provider/provider"
|
||||
import { mapValues } from "remeda"
|
||||
import { mapValues, pipe } from "remeda"
|
||||
import { NamedError } from "@opencode-ai/util/error"
|
||||
import { ModelsDev } from "../provider/models"
|
||||
import { Ripgrep } from "../file/ripgrep"
|
||||
|
|
@ -1025,7 +1025,7 @@ export namespace Server {
|
|||
async (c) => {
|
||||
c.status(204)
|
||||
c.header("Content-Type", "application/json")
|
||||
return stream(c, async (stream) => {
|
||||
return stream(c, async () => {
|
||||
const sessionID = c.req.valid("param").id
|
||||
const body = c.req.valid("json")
|
||||
SessionPrompt.prompt({ ...body, sessionID })
|
||||
|
|
@ -1231,7 +1231,7 @@ export namespace Server {
|
|||
"application/json": {
|
||||
schema: resolver(
|
||||
z.object({
|
||||
providers: ModelsDev.Provider.array(),
|
||||
providers: Provider.Info.array(),
|
||||
default: z.record(z.string(), z.string()),
|
||||
}),
|
||||
),
|
||||
|
|
@ -1242,7 +1242,7 @@ export namespace Server {
|
|||
}),
|
||||
async (c) => {
|
||||
using _ = log.time("providers")
|
||||
const providers = await Provider.list().then((x) => mapValues(x, (item) => item.info))
|
||||
const providers = await Provider.list().then((x) => mapValues(x, (item) => item))
|
||||
return c.json({
|
||||
providers: Object.values(providers),
|
||||
default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),
|
||||
|
|
@ -1272,7 +1272,10 @@ export namespace Server {
|
|||
},
|
||||
}),
|
||||
async (c) => {
|
||||
const providers = await ModelsDev.get()
|
||||
const providers = pipe(
|
||||
await ModelsDev.get(),
|
||||
mapValues((x) => Provider.fromModelsDevProvider(x)),
|
||||
)
|
||||
const connected = await Provider.list().then((x) => Object.keys(x))
|
||||
return c.json({
|
||||
all: Object.values(providers),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { MessageV2 } from "./message-v2"
|
|||
import { SystemPrompt } from "./system"
|
||||
import { Bus } from "../bus"
|
||||
import z from "zod"
|
||||
import type { ModelsDev } from "../provider/models"
|
||||
import { SessionPrompt } from "./prompt"
|
||||
import { Flag } from "../flag/flag"
|
||||
import { Token } from "../util/token"
|
||||
|
|
@ -29,7 +28,7 @@ export namespace SessionCompaction {
|
|||
),
|
||||
}
|
||||
|
||||
export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) {
|
||||
export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: Provider.Model }) {
|
||||
if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false
|
||||
const context = input.model.limit.context
|
||||
if (context === 0) return false
|
||||
|
|
@ -98,6 +97,7 @@ export namespace SessionCompaction {
|
|||
auto: boolean
|
||||
}) {
|
||||
const model = await Provider.getModel(input.model.providerID, input.model.modelID)
|
||||
const language = await Provider.getLanguage(model)
|
||||
const system = [...SystemPrompt.compaction(model.providerID)]
|
||||
const msg = (await Session.updateMessage({
|
||||
id: Identifier.ascending("message"),
|
||||
|
|
@ -126,8 +126,7 @@ export namespace SessionCompaction {
|
|||
const processor = SessionProcessor.create({
|
||||
assistantMessage: msg,
|
||||
sessionID: input.sessionID,
|
||||
providerID: input.model.providerID,
|
||||
model: model.info,
|
||||
model: model,
|
||||
abort: input.abort,
|
||||
})
|
||||
const result = await processor.process({
|
||||
|
|
@ -139,17 +138,13 @@ export namespace SessionCompaction {
|
|||
// set to 0, we handle loop
|
||||
maxRetries: 0,
|
||||
providerOptions: ProviderTransform.providerOptions(
|
||||
model.npm,
|
||||
model.api.npm,
|
||||
model.providerID,
|
||||
pipe(
|
||||
{},
|
||||
mergeDeep(ProviderTransform.options(model.providerID, model.info, model.npm ?? "", input.sessionID)),
|
||||
mergeDeep(model.info.options),
|
||||
),
|
||||
pipe({}, mergeDeep(ProviderTransform.options(model, input.sessionID)), mergeDeep(model.options)),
|
||||
),
|
||||
headers: model.info.headers,
|
||||
headers: model.headers,
|
||||
abortSignal: input.abort,
|
||||
tools: model.info.tool_call ? {} : undefined,
|
||||
tools: model.capabilities.toolcall ? {} : undefined,
|
||||
messages: [
|
||||
...system.map(
|
||||
(x): ModelMessage => ({
|
||||
|
|
@ -183,7 +178,7 @@ export namespace SessionCompaction {
|
|||
},
|
||||
],
|
||||
model: wrapLanguageModel({
|
||||
model: model.language,
|
||||
model: language,
|
||||
middleware: [
|
||||
{
|
||||
async transformParams(args) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { Config } from "../config/config"
|
|||
import { Flag } from "../flag/flag"
|
||||
import { Identifier } from "../id/id"
|
||||
import { Installation } from "../installation"
|
||||
import type { ModelsDev } from "../provider/models"
|
||||
import { Share } from "../share/share"
|
||||
import { Storage } from "../storage/storage"
|
||||
import { Log } from "../util/log"
|
||||
|
|
@ -17,6 +16,7 @@ import { fn } from "@/util/fn"
|
|||
import { Command } from "../command"
|
||||
import { Snapshot } from "@/snapshot"
|
||||
import { ShareNext } from "@/share/share-next"
|
||||
import { Provider } from "@/provider/provider"
|
||||
|
||||
export namespace Session {
|
||||
const log = Log.create({ service: "session" })
|
||||
|
|
@ -389,7 +389,7 @@ export namespace Session {
|
|||
|
||||
export const getUsage = fn(
|
||||
z.object({
|
||||
model: z.custom<ModelsDev.Model>(),
|
||||
model: Provider.Model,
|
||||
usage: z.custom<LanguageModelUsage>(),
|
||||
metadata: z.custom<ProviderMetadata>().optional(),
|
||||
}),
|
||||
|
|
@ -420,16 +420,16 @@ export namespace Session {
|
|||
}
|
||||
|
||||
const costInfo =
|
||||
input.model.cost?.context_over_200k && tokens.input + tokens.cache.read > 200_000
|
||||
? input.model.cost.context_over_200k
|
||||
input.model.cost?.experimentalOver200K && tokens.input + tokens.cache.read > 200_000
|
||||
? input.model.cost.experimentalOver200K
|
||||
: input.model.cost
|
||||
return {
|
||||
cost: safe(
|
||||
new Decimal(0)
|
||||
.add(new Decimal(tokens.input).mul(costInfo?.input ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.output).mul(costInfo?.output ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(costInfo?.cache_read ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(costInfo?.cache_write ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.read).mul(costInfo?.cache.read ?? 0).div(1_000_000))
|
||||
.add(new Decimal(tokens.cache.write).mul(costInfo?.cache.write ?? 0).div(1_000_000))
|
||||
// TODO: update models.dev to have better pricing model, for now:
|
||||
// charge reasoning tokens at the same rate as output tokens
|
||||
.add(new Decimal(tokens.reasoning).mul(costInfo?.output ?? 0).div(1_000_000))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import type { ModelsDev } from "@/provider/models"
|
||||
import { MessageV2 } from "./message-v2"
|
||||
import { streamText } from "ai"
|
||||
import { Log } from "@/util/log"
|
||||
|
|
@ -11,6 +10,7 @@ import { SessionSummary } from "./summary"
|
|||
import { Bus } from "@/bus"
|
||||
import { SessionRetry } from "./retry"
|
||||
import { SessionStatus } from "./status"
|
||||
import type { Provider } from "@/provider/provider"
|
||||
|
||||
export namespace SessionProcessor {
|
||||
const DOOM_LOOP_THRESHOLD = 3
|
||||
|
|
@ -31,8 +31,7 @@ export namespace SessionProcessor {
|
|||
export function create(input: {
|
||||
assistantMessage: MessageV2.Assistant
|
||||
sessionID: string
|
||||
providerID: string
|
||||
model: ModelsDev.Model
|
||||
model: Provider.Model
|
||||
abort: AbortSignal
|
||||
}) {
|
||||
const toolcalls: Record<string, MessageV2.ToolPart> = {}
|
||||
|
|
@ -341,7 +340,7 @@ export namespace SessionProcessor {
|
|||
log.error("process", {
|
||||
error: e,
|
||||
})
|
||||
const error = MessageV2.fromError(e, { providerID: input.providerID })
|
||||
const error = MessageV2.fromError(e, { providerID: input.sessionID })
|
||||
const retry = SessionRetry.retryable(error)
|
||||
if (retry !== undefined) {
|
||||
attempt++
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ import { fn } from "@/util/fn"
|
|||
import { SessionProcessor } from "./processor"
|
||||
import { TaskTool } from "@/tool/task"
|
||||
import { SessionStatus } from "./status"
|
||||
import type { ModelsDev } from "@/provider/models"
|
||||
|
||||
// @ts-ignore
|
||||
globalThis.AI_SDK_LOG_WARNINGS = false
|
||||
|
|
@ -288,6 +287,7 @@ export namespace SessionPrompt {
|
|||
})
|
||||
|
||||
const model = await Provider.getModel(lastUser.model.providerID, lastUser.model.modelID)
|
||||
const language = await Provider.getLanguage(model)
|
||||
const task = tasks.pop()
|
||||
|
||||
// pending subtask
|
||||
|
|
@ -311,7 +311,7 @@ export namespace SessionPrompt {
|
|||
reasoning: 0,
|
||||
cache: { read: 0, write: 0 },
|
||||
},
|
||||
modelID: model.modelID,
|
||||
modelID: model.id,
|
||||
providerID: model.providerID,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
|
|
@ -408,7 +408,7 @@ export namespace SessionPrompt {
|
|||
agent: lastUser.agent,
|
||||
model: {
|
||||
providerID: model.providerID,
|
||||
modelID: model.modelID,
|
||||
modelID: model.id,
|
||||
},
|
||||
sessionID,
|
||||
auto: task.auto,
|
||||
|
|
@ -421,7 +421,7 @@ export namespace SessionPrompt {
|
|||
if (
|
||||
lastFinished &&
|
||||
lastFinished.summary !== true &&
|
||||
SessionCompaction.isOverflow({ tokens: lastFinished.tokens, model: model.info })
|
||||
SessionCompaction.isOverflow({ tokens: lastFinished.tokens, model })
|
||||
) {
|
||||
await SessionCompaction.create({
|
||||
sessionID,
|
||||
|
|
@ -455,7 +455,7 @@ export namespace SessionPrompt {
|
|||
reasoning: 0,
|
||||
cache: { read: 0, write: 0 },
|
||||
},
|
||||
modelID: model.modelID,
|
||||
modelID: model.id,
|
||||
providerID: model.providerID,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
|
|
@ -463,21 +463,18 @@ export namespace SessionPrompt {
|
|||
sessionID,
|
||||
})) as MessageV2.Assistant,
|
||||
sessionID: sessionID,
|
||||
model: model.info,
|
||||
providerID: model.providerID,
|
||||
model,
|
||||
abort,
|
||||
})
|
||||
const system = await resolveSystemPrompt({
|
||||
providerID: model.providerID,
|
||||
model: model.info,
|
||||
model,
|
||||
agent,
|
||||
system: lastUser.system,
|
||||
})
|
||||
const tools = await resolveTools({
|
||||
agent,
|
||||
sessionID,
|
||||
providerID: model.providerID,
|
||||
model: model.info,
|
||||
model,
|
||||
tools: lastUser.tools,
|
||||
processor,
|
||||
})
|
||||
|
|
@ -487,19 +484,19 @@ export namespace SessionPrompt {
|
|||
{
|
||||
sessionID: sessionID,
|
||||
agent: lastUser.agent,
|
||||
model: model.info,
|
||||
model: model,
|
||||
provider,
|
||||
message: lastUser,
|
||||
},
|
||||
{
|
||||
temperature: model.info.temperature
|
||||
? (agent.temperature ?? ProviderTransform.temperature(model.info))
|
||||
temperature: model.capabilities.temperature
|
||||
? (agent.temperature ?? ProviderTransform.temperature(model))
|
||||
: undefined,
|
||||
topP: agent.topP ?? ProviderTransform.topP(model.info),
|
||||
topP: agent.topP ?? ProviderTransform.topP(model),
|
||||
options: pipe(
|
||||
{},
|
||||
mergeDeep(ProviderTransform.options(model.providerID, model.info, model.npm, sessionID, provider?.options)),
|
||||
mergeDeep(model.info.options),
|
||||
mergeDeep(ProviderTransform.options(model, sessionID, provider?.options)),
|
||||
mergeDeep(model.options),
|
||||
mergeDeep(agent.options),
|
||||
),
|
||||
},
|
||||
|
|
@ -547,19 +544,19 @@ export namespace SessionPrompt {
|
|||
"x-opencode-request": lastUser.id,
|
||||
}
|
||||
: undefined),
|
||||
...model.info.headers,
|
||||
...model.headers,
|
||||
},
|
||||
// set to 0, we handle loop
|
||||
maxRetries: 0,
|
||||
activeTools: Object.keys(tools).filter((x) => x !== "invalid"),
|
||||
maxOutputTokens: ProviderTransform.maxOutputTokens(
|
||||
model.providerID,
|
||||
model.api.npm,
|
||||
params.options,
|
||||
model.info.limit.output,
|
||||
model.limit.output,
|
||||
OUTPUT_TOKEN_MAX,
|
||||
),
|
||||
abortSignal: abort,
|
||||
providerOptions: ProviderTransform.providerOptions(model.npm, model.providerID, params.options),
|
||||
providerOptions: ProviderTransform.providerOptions(model.api.npm, model.providerID, params.options),
|
||||
stopWhen: stepCountIs(1),
|
||||
temperature: params.temperature,
|
||||
topP: params.topP,
|
||||
|
|
@ -586,9 +583,9 @@ export namespace SessionPrompt {
|
|||
}),
|
||||
),
|
||||
],
|
||||
tools: model.info.tool_call === false ? undefined : tools,
|
||||
tools: model.capabilities.toolcall === false ? undefined : tools,
|
||||
model: wrapLanguageModel({
|
||||
model: model.language,
|
||||
model: language,
|
||||
middleware: [
|
||||
{
|
||||
async transformParams(args) {
|
||||
|
|
@ -604,7 +601,7 @@ export namespace SessionPrompt {
|
|||
// Transform the inputSchema for provider compatibility
|
||||
return {
|
||||
...tool,
|
||||
inputSchema: ProviderTransform.schema(model.providerID, model.info, tool.inputSchema),
|
||||
inputSchema: ProviderTransform.schema(model, tool.inputSchema),
|
||||
}
|
||||
}
|
||||
// If no inputSchema, return tool unchanged
|
||||
|
|
@ -639,13 +636,8 @@ export namespace SessionPrompt {
|
|||
return Provider.defaultModel()
|
||||
}
|
||||
|
||||
async function resolveSystemPrompt(input: {
|
||||
system?: string
|
||||
agent: Agent.Info
|
||||
providerID: string
|
||||
model: ModelsDev.Model
|
||||
}) {
|
||||
let system = SystemPrompt.header(input.providerID)
|
||||
async function resolveSystemPrompt(input: { system?: string; agent: Agent.Info; model: Provider.Model }) {
|
||||
let system = SystemPrompt.header(input.model.providerID)
|
||||
system.push(
|
||||
...(() => {
|
||||
if (input.system) return [input.system]
|
||||
|
|
@ -663,8 +655,7 @@ export namespace SessionPrompt {
|
|||
|
||||
async function resolveTools(input: {
|
||||
agent: Agent.Info
|
||||
providerID: string
|
||||
model: ModelsDev.Model
|
||||
model: Provider.Model
|
||||
sessionID: string
|
||||
tools?: Record<string, boolean>
|
||||
processor: SessionProcessor.Info
|
||||
|
|
@ -675,9 +666,9 @@ export namespace SessionPrompt {
|
|||
mergeDeep(await ToolRegistry.enabled(input.agent)),
|
||||
mergeDeep(input.tools ?? {}),
|
||||
)
|
||||
for (const item of await ToolRegistry.tools(input.providerID)) {
|
||||
for (const item of await ToolRegistry.tools(input.model.providerID)) {
|
||||
if (Wildcard.all(item.id, enabledTools) === false) continue
|
||||
const schema = ProviderTransform.schema(input.providerID, input.model, z.toJSONSchema(item.parameters))
|
||||
const schema = ProviderTransform.schema(input.model, z.toJSONSchema(item.parameters))
|
||||
tools[item.id] = tool({
|
||||
id: item.id as any,
|
||||
description: item.description,
|
||||
|
|
@ -1428,19 +1419,18 @@ export namespace SessionPrompt {
|
|||
if (!isFirst) return
|
||||
const small =
|
||||
(await Provider.getSmallModel(input.providerID)) ?? (await Provider.getModel(input.providerID, input.modelID))
|
||||
const language = await Provider.getLanguage(small)
|
||||
const provider = await Provider.getProvider(small.providerID)
|
||||
const options = pipe(
|
||||
{},
|
||||
mergeDeep(
|
||||
ProviderTransform.options(small.providerID, small.info, small.npm ?? "", input.session.id, provider?.options),
|
||||
),
|
||||
mergeDeep(ProviderTransform.smallOptions({ providerID: small.providerID, model: small.info })),
|
||||
mergeDeep(small.info.options),
|
||||
mergeDeep(ProviderTransform.options(small, input.session.id, provider?.options)),
|
||||
mergeDeep(ProviderTransform.smallOptions(small)),
|
||||
mergeDeep(small.options),
|
||||
)
|
||||
await generateText({
|
||||
// use higher # for reasoning models since reasoning tokens eat up a lot of the budget
|
||||
maxOutputTokens: small.info.reasoning ? 3000 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small.npm, small.providerID, options),
|
||||
maxOutputTokens: small.capabilities.reasoning ? 3000 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small.api.npm, small.providerID, options),
|
||||
messages: [
|
||||
...SystemPrompt.title(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
|
|
@ -1471,8 +1461,8 @@ export namespace SessionPrompt {
|
|||
},
|
||||
]),
|
||||
],
|
||||
headers: small.info.headers,
|
||||
model: small.language,
|
||||
headers: small.headers,
|
||||
model: language,
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.text)
|
||||
|
|
@ -1489,7 +1479,7 @@ export namespace SessionPrompt {
|
|||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
log.error("failed to generate title", { error, model: small.info.id })
|
||||
log.error("failed to generate title", { error, model: small.id })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,19 +76,20 @@ export namespace SessionSummary {
|
|||
const small =
|
||||
(await Provider.getSmallModel(assistantMsg.providerID)) ??
|
||||
(await Provider.getModel(assistantMsg.providerID, assistantMsg.modelID))
|
||||
const language = await Provider.getLanguage(small)
|
||||
|
||||
const options = pipe(
|
||||
{},
|
||||
mergeDeep(ProviderTransform.options(small.providerID, small.info, small.npm ?? "", assistantMsg.sessionID)),
|
||||
mergeDeep(ProviderTransform.smallOptions({ providerID: small.providerID, model: small.info })),
|
||||
mergeDeep(small.info.options),
|
||||
mergeDeep(ProviderTransform.options(small, assistantMsg.sessionID)),
|
||||
mergeDeep(ProviderTransform.smallOptions(small)),
|
||||
mergeDeep(small.options),
|
||||
)
|
||||
|
||||
const textPart = msgWithParts.parts.find((p) => p.type === "text" && !p.synthetic) as MessageV2.TextPart
|
||||
if (textPart && !userMsg.summary?.title) {
|
||||
const result = await generateText({
|
||||
maxOutputTokens: small.info.reasoning ? 1500 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small.npm, small.providerID, options),
|
||||
maxOutputTokens: small.capabilities.reasoning ? 1500 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small.api.npm, small.providerID, options),
|
||||
messages: [
|
||||
...SystemPrompt.title(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
|
|
@ -106,8 +107,8 @@ export namespace SessionSummary {
|
|||
`,
|
||||
},
|
||||
],
|
||||
headers: small.info.headers,
|
||||
model: small.language,
|
||||
headers: small.headers,
|
||||
model: language,
|
||||
})
|
||||
log.info("title", { title: result.text })
|
||||
userMsg.summary.title = result.text
|
||||
|
|
@ -132,9 +133,9 @@ export namespace SessionSummary {
|
|||
}
|
||||
}
|
||||
const result = await generateText({
|
||||
model: small.language,
|
||||
model: language,
|
||||
maxOutputTokens: 100,
|
||||
providerOptions: ProviderTransform.providerOptions(small.npm, small.providerID, options),
|
||||
providerOptions: ProviderTransform.providerOptions(small.api.npm, small.providerID, options),
|
||||
messages: [
|
||||
...SystemPrompt.summarize(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
|
|
@ -148,7 +149,7 @@ export namespace SessionSummary {
|
|||
content: `Summarize the above conversation according to your system prompts.`,
|
||||
},
|
||||
],
|
||||
headers: small.info.headers,
|
||||
headers: small.headers,
|
||||
}).catch(() => {})
|
||||
if (result) summary = result.text
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import PROMPT_COMPACTION from "./prompt/compaction.txt"
|
|||
import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
import PROMPT_CODEX from "./prompt/codex.txt"
|
||||
import type { ModelsDev } from "@/provider/models"
|
||||
import type { Provider } from "@/provider/provider"
|
||||
|
||||
export namespace SystemPrompt {
|
||||
export function header(providerID: string) {
|
||||
|
|
@ -25,13 +25,13 @@ export namespace SystemPrompt {
|
|||
return []
|
||||
}
|
||||
|
||||
export function provider(model: ModelsDev.Model) {
|
||||
if (model.target.includes("gpt-5")) return [PROMPT_CODEX]
|
||||
if (model.target.includes("gpt-") || model.target.includes("o1") || model.target.includes("o3"))
|
||||
export function provider(model: Provider.Model) {
|
||||
if (model.api.id.includes("gpt-5")) return [PROMPT_CODEX]
|
||||
if (model.api.id.includes("gpt-") || model.api.id.includes("o1") || model.api.id.includes("o3"))
|
||||
return [PROMPT_BEAST]
|
||||
if (model.target.includes("gemini-")) return [PROMPT_GEMINI]
|
||||
if (model.target.includes("claude")) return [PROMPT_ANTHROPIC]
|
||||
if (model.target.includes("polaris-alpha")) return [PROMPT_POLARIS]
|
||||
if (model.api.id.includes("gemini-")) return [PROMPT_GEMINI]
|
||||
if (model.api.id.includes("claude")) return [PROMPT_ANTHROPIC]
|
||||
if (model.api.id.includes("polaris-alpha")) return [PROMPT_POLARIS]
|
||||
return [PROMPT_ANTHROPIC_WITHOUT_TODO]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Bus } from "@/bus"
|
||||
import { Config } from "@/config/config"
|
||||
import { ulid } from "ulid"
|
||||
import type { ModelsDev } from "@/provider/models"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Session } from "@/session"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
|
|
@ -36,7 +35,7 @@ export namespace ShareNext {
|
|||
type: "model",
|
||||
data: [
|
||||
await Provider.getModel(evt.properties.info.model.providerID, evt.properties.info.model.modelID).then(
|
||||
(m) => m.info,
|
||||
(m) => m,
|
||||
),
|
||||
],
|
||||
},
|
||||
|
|
@ -105,7 +104,7 @@ export namespace ShareNext {
|
|||
}
|
||||
| {
|
||||
type: "model"
|
||||
data: ModelsDev.Model[]
|
||||
data: SDK.Model[]
|
||||
}
|
||||
|
||||
const queue = new Map<string, { timeout: NodeJS.Timeout; data: Map<string, Data> }>()
|
||||
|
|
@ -171,7 +170,7 @@ export namespace ShareNext {
|
|||
messages
|
||||
.filter((m) => m.info.role === "user")
|
||||
.map((m) => (m.info as SDK.UserMessage).model)
|
||||
.map((m) => Provider.getModel(m.providerID, m.modelID).then((m) => m.info)),
|
||||
.map((m) => Provider.getModel(m.providerID, m.modelID).then((m) => m)),
|
||||
)
|
||||
await sync(sessionID, [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ export const ReadTool = Tool.define("read", {
|
|||
const modelID = ctx.extra["modelID"] as string
|
||||
const model = await Provider.getModel(providerID, modelID).catch(() => undefined)
|
||||
if (!model) return false
|
||||
return model.info.modalities?.input?.includes("image") ?? false
|
||||
return model.capabilities.input.image
|
||||
})()
|
||||
if (isImage) {
|
||||
if (!supportsImages) {
|
||||
|
|
|
|||
|
|
@ -942,6 +942,76 @@ export type AgentConfig = {
|
|||
| undefined
|
||||
}
|
||||
|
||||
export type ProviderConfig = {
|
||||
api?: string
|
||||
name?: string
|
||||
env?: Array<string>
|
||||
id?: string
|
||||
npm?: string
|
||||
models?: {
|
||||
[key: string]: {
|
||||
id?: string
|
||||
name?: string
|
||||
target?: string
|
||||
release_date?: string
|
||||
attachment?: boolean
|
||||
reasoning?: boolean
|
||||
temperature?: boolean
|
||||
tool_call?: boolean
|
||||
cost?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
context_over_200k?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
}
|
||||
}
|
||||
limit?: {
|
||||
context: number
|
||||
output: number
|
||||
}
|
||||
modalities?: {
|
||||
input: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
}
|
||||
experimental?: boolean
|
||||
status?: "alpha" | "beta" | "deprecated"
|
||||
options?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
}
|
||||
}
|
||||
whitelist?: Array<string>
|
||||
blacklist?: Array<string>
|
||||
options?: {
|
||||
apiKey?: string
|
||||
baseURL?: string
|
||||
/**
|
||||
* GitHub Enterprise URL for copilot authentication
|
||||
*/
|
||||
enterpriseUrl?: string
|
||||
/**
|
||||
* Enable promptCacheKey for this provider (default false)
|
||||
*/
|
||||
setCacheKey?: boolean
|
||||
/**
|
||||
* Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
|
||||
*/
|
||||
timeout?: number | false
|
||||
[key: string]: unknown | string | boolean | (number | false) | undefined
|
||||
}
|
||||
}
|
||||
|
||||
export type McpLocalConfig = {
|
||||
/**
|
||||
* Type of MCP server connection
|
||||
|
|
@ -1100,75 +1170,7 @@ export type Config = {
|
|||
* Custom provider configurations and model overrides
|
||||
*/
|
||||
provider?: {
|
||||
[key: string]: {
|
||||
api?: string
|
||||
name?: string
|
||||
env?: Array<string>
|
||||
id?: string
|
||||
npm?: string
|
||||
models?: {
|
||||
[key: string]: {
|
||||
id?: string
|
||||
name?: string
|
||||
target?: string
|
||||
release_date?: string
|
||||
attachment?: boolean
|
||||
reasoning?: boolean
|
||||
temperature?: boolean
|
||||
tool_call?: boolean
|
||||
cost?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
context_over_200k?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
}
|
||||
}
|
||||
limit?: {
|
||||
context: number
|
||||
output: number
|
||||
}
|
||||
modalities?: {
|
||||
input: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
}
|
||||
experimental?: boolean
|
||||
status?: "alpha" | "beta" | "deprecated"
|
||||
options?: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
}
|
||||
}
|
||||
whitelist?: Array<string>
|
||||
blacklist?: Array<string>
|
||||
options?: {
|
||||
apiKey?: string
|
||||
baseURL?: string
|
||||
/**
|
||||
* GitHub Enterprise URL for copilot authentication
|
||||
*/
|
||||
enterpriseUrl?: string
|
||||
/**
|
||||
* Enable promptCacheKey for this provider (default false)
|
||||
*/
|
||||
setCacheKey?: boolean
|
||||
/**
|
||||
* Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
|
||||
*/
|
||||
timeout?: number | false
|
||||
[key: string]: unknown | string | boolean | (number | false) | undefined
|
||||
}
|
||||
}
|
||||
[key: string]: ProviderConfig
|
||||
}
|
||||
/**
|
||||
* MCP (Model Context Protocol) server configurations
|
||||
|
|
@ -1355,52 +1357,71 @@ export type Command = {
|
|||
|
||||
export type Model = {
|
||||
id: string
|
||||
providerID: string
|
||||
api: {
|
||||
id: string
|
||||
url: string
|
||||
npm: string
|
||||
}
|
||||
name: string
|
||||
target: string
|
||||
release_date: string
|
||||
attachment: boolean
|
||||
reasoning: boolean
|
||||
temperature: boolean
|
||||
tool_call: boolean
|
||||
capabilities: {
|
||||
temperature: boolean
|
||||
reasoning: boolean
|
||||
attachment: boolean
|
||||
toolcall: boolean
|
||||
input: {
|
||||
text: boolean
|
||||
audio: boolean
|
||||
image: boolean
|
||||
video: boolean
|
||||
pdf: boolean
|
||||
}
|
||||
output: {
|
||||
text: boolean
|
||||
audio: boolean
|
||||
image: boolean
|
||||
video: boolean
|
||||
pdf: boolean
|
||||
}
|
||||
}
|
||||
cost: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
context_over_200k?: {
|
||||
cache: {
|
||||
read: number
|
||||
write: number
|
||||
}
|
||||
experimentalOver200K?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
cache: {
|
||||
read: number
|
||||
write: number
|
||||
}
|
||||
}
|
||||
}
|
||||
limit: {
|
||||
context: number
|
||||
output: number
|
||||
}
|
||||
modalities?: {
|
||||
input: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
}
|
||||
experimental?: boolean
|
||||
status?: "alpha" | "beta" | "deprecated"
|
||||
status: "alpha" | "beta" | "deprecated" | "active"
|
||||
options: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
headers: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
}
|
||||
|
||||
export type Provider = {
|
||||
api?: string
|
||||
name: string
|
||||
env: Array<string>
|
||||
id: string
|
||||
npm?: string
|
||||
name: string
|
||||
source: "env" | "config" | "custom" | "api"
|
||||
env: Array<string>
|
||||
key?: string
|
||||
options: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
models: {
|
||||
[key: string]: Model
|
||||
}
|
||||
|
|
@ -2667,7 +2688,56 @@ export type ProviderListResponses = {
|
|||
* List of providers
|
||||
*/
|
||||
200: {
|
||||
all: Array<Provider>
|
||||
all: Array<{
|
||||
api?: string
|
||||
name: string
|
||||
env: Array<string>
|
||||
id: string
|
||||
npm?: string
|
||||
models: {
|
||||
[key: string]: {
|
||||
id: string
|
||||
name: string
|
||||
target: string
|
||||
release_date: string
|
||||
attachment: boolean
|
||||
reasoning: boolean
|
||||
temperature: boolean
|
||||
tool_call: boolean
|
||||
cost: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
context_over_200k?: {
|
||||
input: number
|
||||
output: number
|
||||
cache_read?: number
|
||||
cache_write?: number
|
||||
}
|
||||
}
|
||||
limit: {
|
||||
context: number
|
||||
output: number
|
||||
}
|
||||
modalities?: {
|
||||
input: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
output: Array<"text" | "audio" | "image" | "video" | "pdf">
|
||||
}
|
||||
experimental?: boolean
|
||||
status?: "alpha" | "beta" | "deprecated"
|
||||
options: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
headers?: {
|
||||
[key: string]: string
|
||||
}
|
||||
provider?: {
|
||||
npm: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}>
|
||||
default: {
|
||||
[key: string]: string
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue