Skip to content

Commit d99e2ff

Browse files
edyedy
authored andcommitted
perf: comprehensive optimization across all layers
Batch 1-4 (core/llm/opencode/tui): - Eliminate O(n²) projector rebuild with incremental updates - Add HTTP request timeouts and transport retry logic - Implement streaming compression to avoid blocking event loop - Optimize TUI markdown rendering with incremental updates Batch 5 (protocol/server): - Add bounded queue for PTY WebSocket to prevent memory growth - Harden CORS origin validation and use timing-safe auth comparison Batch 6 (effect-drizzle-sqlite): - Default to immediate transactions to prevent cross-process conflicts - Add statement caching in node driver (LRU 1000) - Consolidate PRAGMA settings to database.ts Batch 7 (infrastructure): - Fix CI trigger branches to include dev - Support OPENCODE_VERSION env var for release builds - Use INSERT OR IGNORE for idempotent migrations - Add workspace.project_id index for query performance All changes maintain backward compatibility and pass typecheck.
1 parent b2a3827 commit d99e2ff

29 files changed

Lines changed: 368 additions & 251 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ name: ci
22

33
on:
44
push:
5-
branches: [main]
5+
branches: [dev, main]
66
pull_request:
7-
branches: [main]
7+
branches: [dev, main]
88

99
jobs:
1010
typecheck:
Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1-
import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"
1+
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core"
22
import { ProjectTable } from "../project/sql"
33
import { ProjectV2 } from "../project"
44
import { WorkspaceV2 } from "../workspace"
55

6-
export const WorkspaceTable = sqliteTable("workspace", {
7-
id: text().$type<WorkspaceV2.ID>().primaryKey(),
8-
type: text().notNull(),
9-
name: text().notNull().default(""),
10-
branch: text(),
11-
directory: text(),
12-
extra: text({ mode: "json" }),
13-
project_id: text()
14-
.$type<ProjectV2.ID>()
15-
.notNull()
16-
.references(() => ProjectTable.id, { onDelete: "cascade" }),
17-
time_used: integer()
18-
.notNull()
19-
.$default(() => Date.now()),
20-
})
6+
export const WorkspaceTable = sqliteTable(
7+
"workspace",
8+
{
9+
id: text().$type<WorkspaceV2.ID>().primaryKey(),
10+
type: text().notNull(),
11+
name: text().notNull().default(""),
12+
branch: text(),
13+
directory: text(),
14+
extra: text({ mode: "json" }),
15+
project_id: text()
16+
.$type<ProjectV2.ID>()
17+
.notNull()
18+
.references(() => ProjectTable.id, { onDelete: "cascade" }),
19+
time_used: integer()
20+
.notNull()
21+
.$default(() => Date.now()),
22+
},
23+
(table) => [index("workspace_project_idx").on(table.project_id)],
24+
)

packages/core/src/database/migration.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export function apply(db: Database) {
3131
)
3232
yield* Effect.forEach(migrations, (migration) =>
3333
tx.run(
34-
sql`INSERT INTO ${sql.identifier("migration")} (id, time_completed) VALUES (${migration.id}, ${Date.now()})`,
34+
sql`INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed) VALUES (${migration.id}, ${Date.now()})`,
3535
),
3636
)
3737
}),
@@ -72,7 +72,7 @@ export function applyOnly(db: Database, input: Migration[]) {
7272
Effect.gen(function* () {
7373
yield* migration.up(tx)
7474
yield* tx.run(
75-
sql`INSERT INTO ${sql.identifier("migration")} (id, time_completed) VALUES (${migration.id}, ${Date.now()})`,
75+
sql`INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed) VALUES (${migration.id}, ${Date.now()})`,
7676
)
7777
}),
7878
)

packages/core/src/database/sqlite.bun.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ const nativeLayer = (config: Config) =>
161161
create: config.create ?? true,
162162
})
163163
yield* Effect.addFinalizer(() => Effect.sync(() => native.close()))
164-
if (config.disableWAL !== true) native.run("PRAGMA journal_mode = WAL;")
165164
return native
166165
}),
167166
)

packages/core/src/database/sqlite.node.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,25 @@ const make = (options: Config) =>
5353
? Statement.defaultTransforms(options.transformResultNames).array
5454
: undefined
5555

56+
const statementCache = new Map<string, ReturnType<typeof native.prepare>>()
57+
const MAX_CACHED_STATEMENTS = 1000
58+
59+
const getStatement = (query: string) => {
60+
let stmt = statementCache.get(query)
61+
if (!stmt) {
62+
if (statementCache.size >= MAX_CACHED_STATEMENTS) {
63+
const firstKey = statementCache.keys().next().value
64+
if (firstKey !== undefined) statementCache.delete(firstKey)
65+
}
66+
stmt = native.prepare(query)
67+
statementCache.set(query, stmt)
68+
}
69+
return stmt
70+
}
71+
5672
const run = (query: string, params: ReadonlyArray<unknown> = []) =>
5773
Effect.withFiber<Array<Record<string, unknown>>, SqlError>((fiber) => {
58-
const statement = native.prepare(query)
74+
const statement = getStatement(query)
5975
statement.setReadBigInts(Context.get(fiber.context, Client.SafeIntegers))
6076
try {
6177
return Effect.succeed(statement.all(...(params as SQLInputValue[])) as Array<Record<string, unknown>>)
@@ -70,7 +86,7 @@ const make = (options: Config) =>
7086

7187
const runValues = (query: string, params: ReadonlyArray<unknown> = []) =>
7288
Effect.withFiber<ReadonlyArray<ReadonlyArray<unknown>>, SqlError>((fiber) => {
73-
const statement = native.prepare(query)
89+
const statement = getStatement(query)
7490
statement.setReadBigInts(Context.get(fiber.context, Client.SafeIntegers))
7591
statement.setReturnArrays(true)
7692
try {
@@ -156,7 +172,6 @@ const nativeLayer = (config: Config) =>
156172
open: true,
157173
})
158174
yield* Effect.addFinalizer(() => Effect.sync(() => native.close()))
159-
if (config.disableWAL !== true && config.readonly !== true) native.exec("PRAGMA journal_mode = WAL;")
160175
return native
161176
}),
162177
)

packages/core/src/session/input.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export * as SessionInput from "./input"
22

33
import { and, asc, eq, isNull, lte } from "drizzle-orm"
44
import { DateTime, Effect, Schema } from "effect"
5+
import { isDeepStrictEqual } from "node:util"
56
import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
67
import type { Database } from "../database/database"
78
import type { EventV2 } from "../event"
@@ -198,8 +199,7 @@ export const equivalent = (
198199
) => input.delivery === expected.delivery && matchesPrompt(input, expected)
199200

200201
const matchesPrompt = (input: Admitted, expected: { readonly sessionID: SessionSchema.ID; readonly prompt: Prompt }) =>
201-
input.sessionID === expected.sessionID &&
202-
JSON.stringify(encodePrompt(input.prompt)) === JSON.stringify(encodePrompt(expected.prompt))
202+
input.sessionID === expected.sessionID && isDeepStrictEqual(encodePrompt(input.prompt), encodePrompt(expected.prompt))
203203

204204
const matchesProjection = (
205205
input: Admitted,

packages/core/src/session/runner/llm.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ const layer = Layer.effect(
229229
const publish = (event: LLMEvent, outputPaths: ReadonlyArray<string> = []) =>
230230
withPublication(publisher.publish(event, outputPaths))
231231
let overflowFailure: ProviderErrorEvent | undefined
232+
let didExecuteHostTool = false
232233
const providerStream = llm.stream(request).pipe(
233234
Stream.runForEach((event) =>
234235
Effect.gen(function* () {
@@ -241,6 +242,7 @@ const layer = Layer.effect(
241242
}
242243
yield* publish(event)
243244
if (event.type !== "tool-call" || event.providerExecuted) return
245+
didExecuteHostTool = true
244246
if (!toolMaterialization) {
245247
yield* withPublication(publisher.failUnsettledTools("Tools are disabled after the maximum agent steps"))
246248
return
@@ -315,7 +317,7 @@ const layer = Layer.effect(
315317
}
316318
const stepSettlement = publisher.stepSettlement()
317319
if (stepSettlement && !publisher.hasProviderError()) {
318-
const endSnapshot = yield* snapshots.capture()
320+
const endSnapshot = didExecuteHostTool ? yield* snapshots.capture() : undefined
319321
const files =
320322
startSnapshot && endSnapshot
321323
? yield* snapshots

packages/effect-drizzle-sqlite/src/effect-sqlite/session.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export class EffectSQLiteSession<TRelations extends AnyRelations> extends SQLite
142142
Effect.flatMap(([scope, connection]) => {
143143
const transaction = this.executeTransactionStatement(
144144
connection,
145-
id === 0 ? `begin ${config?.behavior ?? "deferred"}` : `savepoint effect_sql_${id}`,
145+
id === 0 ? `begin ${config?.behavior ?? "immediate"}` : `savepoint effect_sql_${id}`,
146146
).pipe(
147147
Effect.flatMap(() =>
148148
Effect.provideContext(

packages/llm/src/protocols/openai-responses.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ const onOutputItemAdded = (state: ParserState, event: OpenAIResponsesEvent): Ste
681681
tools: ToolStream.start(state.tools, item.id, {
682682
id: item.call_id ?? item.id,
683683
name: item.name ?? "",
684-
input: item.arguments ?? "",
684+
chunks: item.arguments ? [item.arguments] : [],
685685
providerMetadata,
686686
}),
687687
},

packages/llm/src/protocols/shared.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const optionalNull = <const S extends Schema.Top>(schema: S) => Schema.op
3232
export interface ToolAccumulator {
3333
readonly id: string
3434
readonly name: string
35-
readonly input: string
35+
readonly chunks: ReadonlyArray<string>
3636
}
3737

3838
/**
@@ -171,6 +171,11 @@ export interface ValidatedMedia {
171171
readonly bytes: Uint8Array
172172
}
173173

174+
const VALIDATED_MEDIA_CACHE_LIMIT = 50
175+
const validatedMediaCache = new Map<string, ValidatedMedia>()
176+
177+
const cacheKey = (mime: string, base64: string) => `${mime}\0${base64}`
178+
174179
export const validateMedia = Effect.fn("ProviderShared.validateMedia")(function* (
175180
route: string,
176181
part: MediaPart,
@@ -194,6 +199,10 @@ export const validateMedia = Effect.fn("ProviderShared.validateMedia")(function*
194199
base64 = part.data
195200
}
196201

202+
const key = cacheKey(mime, base64)
203+
const cached = validatedMediaCache.get(key)
204+
if (cached) return cached
205+
197206
if (Buffer.byteLength(base64, "utf8") > MAX_MEDIA_ENCODED_BYTES)
198207
return yield* invalidRequest(`${route} media exceeds the ${MAX_MEDIA_ENCODED_BYTES} byte encoded limit`)
199208
if (!base64 || base64.length % 4 !== 0 || !base64Pattern.test(base64))
@@ -202,7 +211,10 @@ export const validateMedia = Effect.fn("ProviderShared.validateMedia")(function*
202211
if (bytes.byteLength > MAX_MEDIA_DECODED_BYTES)
203212
return yield* invalidRequest(`${route} media exceeds the ${MAX_MEDIA_DECODED_BYTES} byte decoded limit`)
204213
if (bytes.toString("base64") !== base64) return yield* invalidRequest(`${route} media must contain canonical base64`)
205-
return { mime, base64, dataUrl: `data:${mime};base64,${base64}`, bytes } satisfies ValidatedMedia
214+
const result: ValidatedMedia = { mime, base64, dataUrl: `data:${mime};base64,${base64}`, bytes }
215+
if (validatedMediaCache.size >= VALIDATED_MEDIA_CACHE_LIMIT) validatedMediaCache.clear()
216+
validatedMediaCache.set(key, result)
217+
return result
206218
})
207219

208220
export const validateToolFile = (route: string, part: ToolFileContent, supportedMimes: ReadonlySet<string>) =>

0 commit comments

Comments
 (0)