downgrade to ai sdk v4.x

This commit is contained in:
Dax Raad 2025-06-14 18:43:58 -04:00
parent fe109c921e
commit fa1266263d
5 changed files with 249 additions and 155 deletions

View file

@ -91,16 +91,18 @@
},
"catalog": {
"@types/node": "22.13.9",
"ai": "5.0.0-alpha.7",
"ai": "4.3.16",
"typescript": "5.8.2",
"zod": "3.24.2",
},
"packages": {
"@ai-sdk/gateway": ["@ai-sdk/gateway@1.0.0-alpha.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-alpha.7", "@ai-sdk/provider-utils": "3.0.0-alpha.7" }, "peerDependencies": { "zod": "^3.24.0" } }, "sha512-gz1V165eiJnQIexfLyKm11vimrmQ3zdcJhPpjeLFmDU9wrvZwLuklfZ0WgfYSb+EjiP1cKypwt6JSGvWkfKIAQ=="],
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
"@ai-sdk/provider": ["@ai-sdk/provider@2.0.0-alpha.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-lhdrARU3SSmt5p/GNNK7VhazvZpKSCIOjpHUfX7f5jIhVGi/vvlxP1rD6Go57nn1MtuGKNqL04AebSRFDQsQbw=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0-alpha.7", "", { "dependencies": { "@ai-sdk/provider": "2.0.0-alpha.7", "@standard-schema/spec": "^1.0.0", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-AYkT3jskmo7Lwzijo/yHKD1jC+UZizsROO8ULTg9aJZUwR4ABZzAxh4NxDIEy4TWRfBGufp+/9ICHAn6pkU71w=="],
"@ai-sdk/react": ["@ai-sdk/react@1.2.12", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="],
"@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="],
"@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
@ -430,6 +432,8 @@
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
"@types/diff-match-patch": ["@types/diff-match-patch@1.0.36", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="],
"@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
@ -474,7 +478,7 @@
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
"ai": ["ai@5.0.0-alpha.7", "", { "dependencies": { "@ai-sdk/gateway": "1.0.0-alpha.7", "@ai-sdk/provider": "2.0.0-alpha.7", "@ai-sdk/provider-utils": "3.0.0-alpha.7", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-ShCk3frIMdVtK9knvWKiFS7N6Vwnf8mLMv670+T//W9oqfoetSVPBhTF6Dy+oDM/bjVSsBf1BuYImLDvHICOIQ=="],
"ai": ["ai@4.3.16", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g=="],
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
@ -686,6 +690,8 @@
"diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
"diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="],
"direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="],
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
@ -970,6 +976,8 @@
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"jsondiffpatch": ["jsondiffpatch@0.6.0", "", { "dependencies": { "@types/diff-match-patch": "^1.0.36", "chalk": "^5.3.0", "diff-match-patch": "^1.0.5" }, "bin": { "jsondiffpatch": "bin/jsondiffpatch.js" } }, "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ=="],
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"klona": ["klona@2.0.6", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
@ -1274,6 +1282,8 @@
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
@ -1354,6 +1364,8 @@
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
"secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="],
"semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="],
@ -1454,6 +1466,8 @@
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
"swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="],
"tar-fs": ["tar-fs@3.0.9", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA=="],
"tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
@ -1462,6 +1476,8 @@
"thread-stream": ["thread-stream@0.15.2", "", { "dependencies": { "real-require": "^0.1.0" } }, "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA=="],
"throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
"tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
@ -1546,6 +1562,8 @@
"url": ["url@0.10.3", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
"use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],

View file

@ -15,7 +15,7 @@
"typescript": "5.8.2",
"@types/node": "22.13.9",
"zod": "3.24.2",
"ai": "5.0.0-alpha.7"
"ai": "4.3.16"
}
},
"devDependencies": {

View file

@ -44,15 +44,24 @@ export namespace BunProc {
}),
)
export async function install(pkg: string, version = "latest") {
const dir = path.join(Global.Path.cache, `node_modules`, pkg)
if (!(await Bun.file(path.join(dir, "package.json")).exists())) {
log.info("installing", { pkg })
await BunProc.run(["add", `${pkg}@${version}`], {
cwd: Global.Path.cache,
}).catch(() => {
throw new InstallFailedError({ pkg, version })
})
}
return dir
const mod = path.join(Global.Path.cache, "node_modules", pkg)
const pkgjson = Bun.file(path.join(Global.Path.cache, "package.json"))
const parsed = await pkgjson.json().catch(() => ({
dependencies: {},
}))
if (parsed.dependencies[pkg] === version) return mod
parsed.dependencies[pkg] = version
await Bun.write(pkgjson, JSON.stringify(parsed, null, 2))
await BunProc.run(["install"], {
cwd: Global.Path.cache,
}).catch((e) => {
new InstallFailedError(
{ pkg, version },
{
cause: e,
},
)
})
return mod
}
}

View file

@ -86,7 +86,7 @@ export namespace ModelsDev {
export async function pkg(providerID: string): Promise<[string, string]> {
const packages = await aisdk()
const match = packages[`@ai-sdk/${providerID}`]
if (match) return [match.package.name, "alpha"]
if (match) return [match.package.name, "latest"]
return [providerID, "latest"]
}
}

View file

@ -4,15 +4,15 @@ import { Identifier } from "../id/id"
import { Storage } from "../storage/storage"
import { Log } from "../util/log"
import {
convertToModelMessages,
generateText,
LoadAPIKeyError,
stepCountIs,
streamText,
tool,
type Tool as AITool,
type LanguageModelUsage,
type UIMessage,
type CoreMessage,
type UserContent,
type AssistantContent,
} from "ai"
import { z, ZodSchema } from "zod"
import { Decimal } from "decimal.js"
@ -28,6 +28,7 @@ import { NamedError } from "../util/error"
import type { Tool } from "../tool/tool"
import { SystemPrompt } from "./system"
import { Flag } from "../flag/flag"
import type { ModelsDev } from "../provider/models"
export namespace Session {
const log = Log.create({ service: "session" })
@ -227,34 +228,28 @@ export namespace Session {
const session = await get(input.sessionID)
if (msgs.length === 0 && !session.parentID) {
generateText({
maxOutputTokens: 20,
messages: convertToModelMessages([
maxTokens: input.providerID === "google" ? 1024 : 20,
messages: [
...SystemPrompt.title(input.providerID).map(
(x): UIMessage => ({
id: Identifier.ascending("message"),
(x): CoreMessage => ({
role: "system",
parts: [
{
type: "text",
text: x,
},
],
content: x,
}),
),
{
role: "user",
parts: input.parts,
content: toUserContent(input.parts),
},
]),
temperature: 0,
],
model: model.language,
})
.then((result) => {
return Session.update(input.sessionID, (draft) => {
draft.title = result.text
})
if (result.text)
return Session.update(input.sessionID, (draft) => {
draft.title = result.text
})
})
.catch(() => {})
.catch((e) => {})
}
const msg: Message.Info = {
role: "user",
@ -400,90 +395,9 @@ export namespace Session {
}
text = undefined
},
async onChunk(input) {
const value = input.chunk
l.info("part", {
type: value.type,
})
switch (value.type) {
case "text":
if (!text) {
text = value
next.parts.push(value)
break
} else text.text += value.text
break
case "tool-call": {
const [match] = next.parts.flatMap((p) =>
p.type === "tool-invocation" &&
p.toolInvocation.toolCallId === value.toolCallId
? [p]
: [],
)
if (!match) break
match.toolInvocation.args = value.args
match.toolInvocation.state = "call"
Bus.publish(Message.Event.PartUpdated, {
part: match,
messageID: next.id,
sessionID: next.metadata.sessionID,
})
break
}
case "tool-call-streaming-start":
next.parts.push({
type: "tool-invocation",
toolInvocation: {
state: "partial-call",
toolName: value.toolName,
toolCallId: value.toolCallId,
args: {},
},
})
Bus.publish(Message.Event.PartUpdated, {
part: next.parts[next.parts.length - 1],
messageID: next.id,
sessionID: next.metadata.sessionID,
})
break
case "tool-call-delta":
break
case "tool-result":
const match = next.parts.find(
(p) =>
p.type === "tool-invocation" &&
p.toolInvocation.toolCallId === value.toolCallId,
)
if (match && match.type === "tool-invocation") {
match.toolInvocation = {
args: value.args,
toolCallId: value.toolCallId,
toolName: value.toolName,
state: "result",
result: value.result as string,
}
Bus.publish(Message.Event.PartUpdated, {
part: match,
messageID: next.id,
sessionID: next.metadata.sessionID,
})
}
break
default:
l.info("unhandled", {
type: value.type,
})
}
await updateMessage(next)
},
async onFinish(input) {
const assistant = next.metadata!.assistant!
const usage = getUsage(input.totalUsage, model.info)
const usage = getUsage(input.usage, model.info)
assistant.cost = usage.cost
await updateMessage(next)
},
@ -515,31 +429,44 @@ export namespace Session {
error: next.metadata.error,
})
},
async prepareStep(step) {
next.parts.push({
type: "step-start",
})
await updateMessage(next)
return step
},
// async prepareStep(step) {
// next.parts.push({
// type: "step-start",
// })
// await updateMessage(next)
// return step
// },
toolCallStreaming: true,
abortSignal: abort.signal,
stopWhen: stepCountIs(1000),
messages: convertToModelMessages([
maxSteps: 1000,
messages: [
...system.map(
(x): UIMessage => ({
id: Identifier.ascending("message"),
(x): CoreMessage => ({
role: "system",
parts: [
{
type: "text",
text: x,
},
],
content: x,
}),
),
...msgs,
]),
...msgs.flatMap((msg): CoreMessage[] => {
switch (msg.role) {
case "user":
return [
{
role: "user",
content: toUserContent(msg.parts),
},
]
case "assistant":
return [
{
role: "assistant",
content: toAssistantContent(msg.parts),
},
]
default:
return []
}
}),
],
temperature: model.info.id === "codex-mini-latest" ? undefined : 0,
tools: {
...(await MCP.tools()),
@ -547,6 +474,101 @@ export namespace Session {
},
model: model.language,
})
for await (const value of result.fullStream) {
l.info("part", {
type: value.type,
})
switch (value.type) {
case "step-start":
next.parts.push({
type: "step-start",
})
break
case "text-delta":
if (!text) {
text = {
type: "text",
text: value.textDelta,
}
next.parts.push(text)
break
} else text.text += value.textDelta
break
case "tool-call": {
const [match] = next.parts.flatMap((p) =>
p.type === "tool-invocation" &&
p.toolInvocation.toolCallId === value.toolCallId
? [p]
: [],
)
if (!match) break
match.toolInvocation.args = value.args
match.toolInvocation.state = "call"
Bus.publish(Message.Event.PartUpdated, {
part: match,
messageID: next.id,
sessionID: next.metadata.sessionID,
})
break
}
case "tool-call-streaming-start":
next.parts.push({
type: "tool-invocation",
toolInvocation: {
state: "partial-call",
toolName: value.toolName,
toolCallId: value.toolCallId,
args: {},
},
})
Bus.publish(Message.Event.PartUpdated, {
part: next.parts[next.parts.length - 1],
messageID: next.id,
sessionID: next.metadata.sessionID,
})
break
case "tool-call-delta":
break
// for some reason ai sdk claims to not send this part but it does
// @ts-expect-error
case "tool-result":
const match = next.parts.find(
(p) =>
p.type === "tool-invocation" &&
// @ts-expect-error
p.toolInvocation.toolCallId === value.toolCallId,
)
if (match && match.type === "tool-invocation") {
match.toolInvocation = {
// @ts-expect-error
args: value.args,
// @ts-expect-error
toolCallId: value.toolCallId,
// @ts-expect-error
toolName: value.toolName,
state: "result",
// @ts-expect-error
result: value.result as string,
}
Bus.publish(Message.Event.PartUpdated, {
part: match,
messageID: next.id,
sessionID: next.metadata.sessionID,
})
}
break
default:
l.info("unhandled", {
type: value.type,
})
}
await updateMessage(next)
}
await result.consumeStream({
onError: (err) => {
log.error("stream error", {
@ -618,30 +640,23 @@ export namespace Session {
const result = await generateText({
abortSignal: abort.signal,
model: model.language,
messages: convertToModelMessages([
messages: [
...system.map(
(x): UIMessage => ({
id: Identifier.ascending("message"),
(x): CoreMessage => ({
role: "system",
parts: [
{
type: "text",
text: x,
},
],
content: x,
}),
),
...filtered,
{
role: "user",
parts: [
content: toUserContent([
{
type: "text",
text: "Provide a detailed but concise summary of our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next.",
},
],
]),
},
]),
],
})
next.parts.push({
type: "text",
@ -669,11 +684,11 @@ export namespace Session {
}
}
function getUsage(usage: LanguageModelUsage, model: Provider.Model) {
function getUsage(usage: LanguageModelUsage, model: ModelsDev.Model) {
const tokens = {
input: usage.inputTokens ?? 0,
output: usage.outputTokens ?? 0,
reasoning: usage.reasoningTokens ?? 0,
input: usage.promptTokens ?? 0,
output: usage.completionTokens ?? 0,
reasoning: 0,
}
return {
cost: new Decimal(0)
@ -710,3 +725,55 @@ export namespace Session {
await App.initialize()
}
}
function toAssistantContent(parts: Message.Part[]): AssistantContent {
const result: AssistantContent = []
for (const part of parts) {
switch (part.type) {
case "text":
result.push({ type: "text", text: part.text })
break
case "file":
result.push({
type: "file",
data: new URL(part.url),
mimeType: part.mediaType,
filename: part.filename,
})
break
case "tool-invocation":
result.push({
type: "tool-call",
args: part.toolInvocation.args,
toolName: part.toolInvocation.toolName,
toolCallId: part.toolInvocation.toolCallId,
})
break
default:
break
}
}
return result
}
function toUserContent(parts: Message.Part[]): UserContent {
const result: UserContent = []
for (const part of parts) {
switch (part.type) {
case "text":
return [{ type: "text", text: part.text }]
case "file":
return [
{
type: "file",
filename: part.filename,
data: new URL(part.url),
mimeType: part.mediaType,
},
]
default:
return []
}
}
return result
}