summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-06-14 18:43:58 -0400
committerDax Raad <[email protected]>2025-06-14 18:44:08 -0400
commitfa1266263da67e5f577068822c08fe140c95679d (patch)
treed482977b9c74f970ae4ee0651575ea40831ef105
parentfe109c921eac9dbcf6459c1e1be66e66f2d0dc11 (diff)
downloadopencode-fa1266263da67e5f577068822c08fe140c95679d.tar.gz
opencode-fa1266263da67e5f577068822c08fe140c95679d.zip
downgrade to ai sdk v4.x
-rw-r--r--bun.lock28
-rw-r--r--package.json2
-rw-r--r--packages/opencode/src/bun/index.ts29
-rw-r--r--packages/opencode/src/provider/models.ts2
-rw-r--r--packages/opencode/src/session/index.ts343
5 files changed, 249 insertions, 155 deletions
diff --git a/bun.lock b/bun.lock
index f2547af40..165211228 100644
--- a/bun.lock
+++ b/bun.lock
@@ -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/[email protected]", "", { "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/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
- "@ai-sdk/provider": ["@ai-sdk/[email protected]", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-lhdrARU3SSmt5p/GNNK7VhazvZpKSCIOjpHUfX7f5jIhVGi/vvlxP1rD6Go57nn1MtuGKNqL04AebSRFDQsQbw=="],
+ "@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "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/[email protected]", "", { "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/[email protected]", "", { "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/[email protected]", "", { "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/[email protected]", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="],
@@ -430,6 +432,8 @@
"@types/debug": ["@types/[email protected]", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
+ "@types/diff-match-patch": ["@types/[email protected]", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="],
+
"@types/estree": ["@types/[email protected]", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
"@types/estree-jsx": ["@types/[email protected]", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
@@ -474,7 +478,7 @@
"acorn-walk": ["[email protected]", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
- "ai": ["[email protected]", "", { "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": ["[email protected]", "", { "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": ["[email protected]", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
@@ -686,6 +690,8 @@
"diff": ["[email protected]", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="],
+ "diff-match-patch": ["[email protected]", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="],
+
"direction": ["[email protected]", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="],
"dlv": ["[email protected]", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
@@ -970,6 +976,8 @@
"json5": ["[email protected]", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
+ "jsondiffpatch": ["[email protected]", "", { "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": ["[email protected]", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
"klona": ["[email protected]", "", {}, "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="],
@@ -1274,6 +1282,8 @@
"rc": ["[email protected]", "", { "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": ["[email protected]", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+
"readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdirp": ["[email protected]", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
@@ -1354,6 +1364,8 @@
"sax": ["[email protected]", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
+ "secure-json-parse": ["[email protected]", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="],
+
"semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
"send": ["[email protected]", "", { "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": ["[email protected]", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+ "swr": ["[email protected]", "", { "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": ["[email protected]", "", { "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": ["[email protected]", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="],
@@ -1462,6 +1476,8 @@
"thread-stream": ["[email protected]", "", { "dependencies": { "real-require": "^0.1.0" } }, "sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA=="],
+ "throttleit": ["[email protected]", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="],
+
"tiny-inflate": ["[email protected]", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
"tinyexec": ["[email protected]", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="],
@@ -1546,6 +1562,8 @@
"url": ["[email protected]", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
+ "use-sync-external-store": ["[email protected]", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="],
+
"util": ["[email protected]", "", { "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": ["[email protected]", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
diff --git a/package.json b/package.json
index 0f4dc8c43..f454d7d9f 100644
--- a/package.json
+++ b/package.json
@@ -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": {
diff --git a/packages/opencode/src/bun/index.ts b/packages/opencode/src/bun/index.ts
index fc8e7e839..a003f1c5d 100644
--- a/packages/opencode/src/bun/index.ts
+++ b/packages/opencode/src/bun/index.ts
@@ -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
}
}
diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts
index f6b2653d6..334b11914 100644
--- a/packages/opencode/src/provider/models.ts
+++ b/packages/opencode/src/provider/models.ts
@@ -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"]
}
}
diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts
index 3cf2a383e..43f6720fc 100644
--- a/packages/opencode/src/session/index.ts
+++ b/packages/opencode/src/session/index.ts
@@ -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
+}