Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function Sidebar(props: { sessionID: string; overlay?: boolean }) {
)

const cost = createMemo(() => {
const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
const total = session().cost ?? messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0)
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
Expand Down
14 changes: 14 additions & 0 deletions packages/opencode/src/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export namespace Session {
.optional(),
title: z.string(),
version: z.string(),
cost: z.number().optional(),
time: z.object({
created: z.number(),
updated: z.number(),
Expand Down Expand Up @@ -199,6 +200,7 @@ export namespace Session {
directory: input.directory,
parentID: input.parentID,
title: input.title ?? createDefaultTitle(!!input.parentID),
cost: 0,
permission: input.permission,
time: {
created: Date.now(),
Expand Down Expand Up @@ -236,6 +238,11 @@ export namespace Session {
return Storage.read<ShareInfo>(["share", id])
})

export const getCost = fn(Identifier.schema("session"), async (id) => {
const read = await Storage.read<Info>(["session", Instance.project.id, id])
return read.cost
})

export const share = fn(Identifier.schema("session"), async (id) => {
const cfg = await Config.get()
if (cfg.share === "disabled") {
Expand Down Expand Up @@ -272,6 +279,13 @@ export namespace Session {
return result
}

export async function addCost(sessionID: string, amount: number) {
if (amount === 0) return
await update(sessionID, (draft) => {
draft.cost = (draft.cost ?? 0) + amount
})
}

export const diff = fn(Identifier.schema("session"), async (sessionID) => {
const diffs = await Storage.read<Snapshot.FileDiff[]>(["session_diff", sessionID])
return diffs ?? []
Expand Down
1 change: 1 addition & 0 deletions packages/opencode/src/session/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export namespace SessionProcessor {
cost: usage.cost,
})
await Session.updateMessage(input.assistantMessage)
await Session.addCost(input.sessionID, usage.cost)
if (snapshot) {
const patch = await Snapshot.patch(snapshot)
if (patch.files.length) {
Expand Down
46 changes: 46 additions & 0 deletions packages/opencode/src/storage/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,52 @@ export namespace Storage {
)
}
},
async (dir) => {
log.info("migrating session costs")
const startTime = Date.now()
let migratedCount = 0

for await (const sessionPath of new Bun.Glob("session/*/*.json").scan({
cwd: dir,
absolute: true,
})) {
const session = await Bun.file(sessionPath).json()
if (!session) continue
if (session.cost != null) continue

let totalCost = 0
const messageDir = path.join(dir, "message", session.id)

if (await fs.exists(messageDir)) {
for await (const messagePath of new Bun.Glob("*.json").scan({
cwd: messageDir,
absolute: true,
})) {
const message = await Bun.file(messagePath).json()
if (message.role !== "assistant") continue
totalCost += message.cost ?? 0
}
}

await Bun.file(sessionPath).write(
JSON.stringify(
{
...session,
cost: totalCost,
},
null,
2,
),
)

migratedCount++
}

log.info("cost migration complete", {
sessionsMigrated: migratedCount,
elapsedMs: Date.now() - startTime,
})
},
]

const state = lazy(async () => {
Expand Down