Merge branch 'dev' into feat/canceled-prompts-in-history

This commit is contained in:
Ariane Emory
2026-02-14 19:21:47 -05:00
28 changed files with 703 additions and 88 deletions
+99 -11
View File
@@ -171,7 +171,7 @@ export namespace ProviderTransform {
return msgs
}
function applyCaching(msgs: ModelMessage[], providerID: string): ModelMessage[] {
function applyCaching(msgs: ModelMessage[], model: Provider.Model): ModelMessage[] {
const system = msgs.filter((msg) => msg.role === "system").slice(0, 2)
const final = msgs.filter((msg) => msg.role !== "system").slice(-2)
@@ -194,7 +194,7 @@ export namespace ProviderTransform {
}
for (const msg of unique([...system, ...final])) {
const useMessageLevelOptions = providerID === "anthropic" || providerID.includes("bedrock")
const useMessageLevelOptions = model.providerID === "anthropic" || model.providerID.includes("bedrock")
const shouldUseContentOptions = !useMessageLevelOptions && Array.isArray(msg.content) && msg.content.length > 0
if (shouldUseContentOptions) {
@@ -253,14 +253,15 @@ export namespace ProviderTransform {
msgs = unsupportedParts(msgs, model)
msgs = normalizeMessages(msgs, model, options)
if (
model.providerID === "anthropic" ||
model.api.id.includes("anthropic") ||
model.api.id.includes("claude") ||
model.id.includes("anthropic") ||
model.id.includes("claude") ||
model.api.npm === "@ai-sdk/anthropic"
(model.providerID === "anthropic" ||
model.api.id.includes("anthropic") ||
model.api.id.includes("claude") ||
model.id.includes("anthropic") ||
model.id.includes("claude") ||
model.api.npm === "@ai-sdk/anthropic") &&
model.api.npm !== "@ai-sdk/gateway"
) {
msgs = applyCaching(msgs, model.providerID)
msgs = applyCaching(msgs, model)
}
// Remap providerOptions keys from stored providerID to expected SDK key
@@ -360,11 +361,53 @@ export namespace ProviderTransform {
switch (model.api.npm) {
case "@openrouter/ai-sdk-provider":
if (!model.id.includes("gpt") && !model.id.includes("gemini-3")) return {}
if (!model.id.includes("gpt") && !model.id.includes("gemini-3") && !model.id.includes("claude")) return {}
return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoning: { effort } }]))
// TODO: YOU CANNOT SET max_tokens if this is set!!!
case "@ai-sdk/gateway":
if (model.id.includes("anthropic")) {
return {
high: {
thinking: {
type: "enabled",
budgetTokens: 16000,
},
},
max: {
thinking: {
type: "enabled",
budgetTokens: 31999,
},
},
}
}
if (model.id.includes("google")) {
if (id.includes("2.5")) {
return {
high: {
thinkingConfig: {
includeThoughts: true,
thinkingBudget: 16000,
},
},
max: {
thinkingConfig: {
includeThoughts: true,
thinkingBudget: 24576,
},
},
}
}
return Object.fromEntries(
["low", "high"].map((effort) => [
effort,
{
includeThoughts: true,
thinkingLevel: effort,
},
]),
)
}
return Object.fromEntries(OPENAI_EFFORTS.map((effort) => [effort, { reasoningEffort: effort }]))
case "@ai-sdk/github-copilot":
@@ -720,6 +763,15 @@ export namespace ProviderTransform {
result["promptCacheKey"] = input.sessionID
}
if (input.model.providerID === "openrouter") {
result["prompt_cache_key"] = input.sessionID
}
if (input.model.api.npm === "@ai-sdk/gateway") {
result["gateway"] = {
caching: "auto",
}
}
return result
}
@@ -753,7 +805,43 @@ export namespace ProviderTransform {
return {}
}
// Maps model ID prefix to provider slug used in providerOptions.
// Example: "amazon/nova-2-lite" → "bedrock"
const SLUG_OVERRIDES: Record<string, string> = {
amazon: "bedrock",
}
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
if (model.api.npm === "@ai-sdk/gateway") {
// Gateway providerOptions are split across two namespaces:
// - `gateway`: gateway-native routing/caching controls (order, only, byok, etc.)
// - `<upstream slug>`: provider-specific model options (anthropic/openai/...)
// We keep `gateway` as-is and route every other top-level option under the
// model-derived upstream slug.
const i = model.api.id.indexOf("/")
const rawSlug = i > 0 ? model.api.id.slice(0, i) : undefined
const slug = rawSlug ? (SLUG_OVERRIDES[rawSlug] ?? rawSlug) : undefined
const gateway = options.gateway
const rest = Object.fromEntries(Object.entries(options).filter(([k]) => k !== "gateway"))
const has = Object.keys(rest).length > 0
const result: Record<string, any> = {}
if (gateway !== undefined) result.gateway = gateway
if (has) {
if (slug) {
// Route model-specific options under the provider slug
result[slug] = rest
} else if (gateway && typeof gateway === "object" && !Array.isArray(gateway)) {
result.gateway = { ...gateway, ...rest }
} else {
result.gateway = rest
}
}
return result
}
const key = sdkKey(model.api.npm) ?? model.providerID
return { [key]: options }
}
@@ -53,15 +53,15 @@ export const SessionRoutes = lazy(() =>
),
async (c) => {
const query = c.req.valid("query")
const term = query.search?.toLowerCase()
const sessions: Session.Info[] = []
for await (const session of Session.list()) {
if (query.directory !== undefined && session.directory !== query.directory) continue
if (query.roots && session.parentID) continue
if (query.start !== undefined && session.time.updated < query.start) continue
if (term !== undefined && !session.title.toLowerCase().includes(term)) continue
for await (const session of Session.list({
directory: query.directory,
roots: query.roots,
start: query.start,
search: query.search,
limit: query.limit,
})) {
sessions.push(session)
if (query.limit !== undefined && sessions.length >= query.limit) break
}
return c.json(sessions)
},
+28 -10
View File
@@ -10,7 +10,7 @@ import { Flag } from "../flag/flag"
import { Identifier } from "../id/id"
import { Installation } from "../installation"
import { Database, NotFoundError, eq, and, or, like } from "../storage/db"
import { Database, NotFoundError, eq, and, or, gte, isNull, desc, like } from "../storage/db"
import { SessionTable, MessageTable, PartTable } from "./session.sql"
import { Storage } from "@/storage/storage"
import { Log } from "../util/log"
@@ -505,20 +505,38 @@ export namespace Session {
},
)
export function* list() {
export function* list(input?: {
directory?: string
roots?: boolean
start?: number
search?: string
limit?: number
}) {
const project = Instance.project
// const rel = path.relative(Instance.worktree, Instance.directory)
// const suffix = path.sep + rel
const conditions = [eq(SessionTable.project_id, project.id)]
if (input?.directory) {
conditions.push(eq(SessionTable.directory, input.directory))
}
if (input?.roots) {
conditions.push(isNull(SessionTable.parent_id))
}
if (input?.start) {
conditions.push(gte(SessionTable.time_updated, input.start))
}
if (input?.search) {
conditions.push(like(SessionTable.title, `%${input.search}%`))
}
const limit = input?.limit ?? 100
const rows = Database.use((db) =>
db
.select()
.from(SessionTable)
.where(
and(
eq(SessionTable.project_id, project.id),
// or(eq(SessionTable.directory, Instance.directory), like(SessionTable.directory, `%${suffix}`)),
),
)
.where(and(...conditions))
.orderBy(desc(SessionTable.time_updated))
.limit(limit)
.all(),
)
for (const row of rows) {
+1
View File
@@ -74,6 +74,7 @@ export namespace Database {
sqlite.run("PRAGMA busy_timeout = 5000")
sqlite.run("PRAGMA cache_size = -64000")
sqlite.run("PRAGMA foreign_keys = ON")
sqlite.run("PRAGMA wal_checkpoint(PASSIVE)")
const db = drizzle({ client: sqlite, schema })