Просмотр исходного кода

Merge branch 'dev' into vertex-rep-v1

Filip 1 день назад
Родитель
Сommit
70a48bb667

+ 3 - 3
packages/app/src/components/dialog-select-server.tsx

@@ -145,7 +145,7 @@ function ServerForm(props: ServerFormProps) {
           type="text"
           label={language.t("dialog.server.add.name")}
           placeholder={language.t("dialog.server.add.namePlaceholder")}
-          value={props.name}
+          defaultValue={props.name}
           disabled={props.busy}
           onChange={props.onNameChange}
           onKeyDown={keyDown}
@@ -155,7 +155,7 @@ function ServerForm(props: ServerFormProps) {
             type="text"
             label={language.t("dialog.server.add.username")}
             placeholder={language.t("dialog.server.add.usernamePlaceholder")}
-            value={props.username}
+            defaultValue={props.username}
             disabled={props.busy}
             onChange={props.onUsernameChange}
             onKeyDown={keyDown}
@@ -164,7 +164,7 @@ function ServerForm(props: ServerFormProps) {
             type="password"
             label={language.t("dialog.server.add.password")}
             placeholder={language.t("dialog.server.add.passwordPlaceholder")}
-            value={props.password}
+            defaultValue={props.password}
             disabled={props.busy}
             onChange={props.onPasswordChange}
             onKeyDown={keyDown}

+ 1 - 1
packages/console/app/src/routes/zen/util/handler.ts

@@ -247,7 +247,7 @@ export async function handler(
             headers.delete("host")
             headers.delete("content-length")
             headers.delete("x-opencode-request")
-            headers.delete("x-opencode-session")
+            if (!isNewInference) headers.delete("x-opencode-session")
             headers.delete("x-opencode-project")
             headers.delete("x-opencode-client")
             return headers

+ 1 - 0
packages/core/src/session/compaction.ts

@@ -202,6 +202,7 @@ export const make = (dependencies: Dependencies) => {
       .stream(
         LLM.request({
           model: input.model,
+          http: input.request.http,
           messages: [Message.user(summaryPrompt)],
           tools: [],
           generation: { maxTokens: summaryOutput },

+ 7 - 0
packages/core/src/session/runner/llm.ts

@@ -204,6 +204,13 @@ const layer = Layer.effect(
       const promptCacheKey = /^ses_[0-9a-f]{64}$/.test(session.id) ? session.id.slice(4) : session.id
       const request = LLM.request({
         model,
+        http: {
+          headers: {
+            "x-session-affinity": session.id,
+            "X-Session-Id": session.id,
+            ...(session.parentID ? { "x-parent-session-id": session.parentID } : {}),
+          },
+        },
         providerOptions: { openai: { promptCacheKey } },
         system: [agent.info?.system, system.baseline]
           .filter((part): part is string => part !== undefined && part.length > 0)

+ 47 - 0
packages/core/test/session-runner.test.ts

@@ -1101,6 +1101,16 @@ describe("SessionRunnerLLM", () => {
       yield* session.resume(sessionID)
 
       expect(requests).toHaveLength(2)
+      expect(requests.map((request) => request.http?.headers)).toEqual([
+        {
+          "x-session-affinity": sessionID,
+          "X-Session-Id": sessionID,
+        },
+        {
+          "x-session-affinity": sessionID,
+          "X-Session-Id": sessionID,
+        },
+      ])
       expect(userTexts(requests[0])[0]).toContain("## Objective")
       expect(userTexts(requests[1])).toHaveLength(1)
       expect(userTexts(requests[1])[0]).toContain("<summary>\n## Objective\n- Preserve the task\n</summary>")
@@ -2508,6 +2518,43 @@ describe("SessionRunnerLLM", () => {
     }),
   )
 
+  it.effect("adds session correlation headers to model requests", () =>
+    Effect.gen(function* () {
+      yield* setup
+      const session = yield* SessionV2.Service
+      yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run correlated request" }), resume: false })
+
+      requests.length = 0
+      yield* session.resume(sessionID)
+
+      expect(requests[0]?.http?.headers).toEqual({
+        "x-session-affinity": sessionID,
+        "X-Session-Id": sessionID,
+      })
+    }),
+  )
+
+  it.effect("adds the parent session header to child model requests", () =>
+    Effect.gen(function* () {
+      yield* setup
+      const session = yield* SessionV2.Service
+      const parentID = SessionV2.ID.make("ses_runner_parent")
+      const { db } = yield* Database.Service
+      yield* db
+        .update(SessionTable)
+        .set({ parent_id: parentID })
+        .where(eq(SessionTable.id, sessionID))
+        .run()
+        .pipe(Effect.orDie)
+      yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Run child request" }), resume: false })
+
+      requests.length = 0
+      yield* session.resume(sessionID)
+
+      expect(requests[0]?.http?.headers?.["x-parent-session-id"]).toBe(parentID)
+    }),
+  )
+
   it.effect("bounds 64-character session prompt cache keys", () =>
     Effect.gen(function* () {
       yield* setup

+ 4 - 1
packages/opencode/src/server/routes/instance/httpapi/handlers/provider.ts

@@ -2,6 +2,7 @@ import { ProviderAuth } from "@/provider/auth"
 import { Config } from "@/config/config"
 import { ModelsDev } from "@opencode-ai/core/models-dev"
 import { Provider } from "@/provider/provider"
+import { Auth } from "@/auth"
 
 import { mapValues } from "remeda"
 import { Effect, Schema } from "effect"
@@ -36,6 +37,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
     const cfg = yield* Config.Service
     const provider = yield* Provider.Service
     const svc = yield* ProviderAuth.Service
+    const authStore = yield* Auth.Service
 
     const list = Effect.fn("ProviderHttpApi.list")(function* () {
       const config = yield* cfg.get()
@@ -47,6 +49,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
         if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) filtered[key] = value
       }
       const connected = yield* provider.list()
+      const credentials = yield* authStore.all().pipe(Effect.orDie)
       const providers = Object.assign(
         mapValues(filtered, (item) => Provider.fromModelsDevProvider(item)),
         connected,
@@ -54,7 +57,7 @@ export const providerHandlers = HttpApiBuilder.group(InstanceHttpApi, "provider"
       return {
         all: Object.values(providers).map(Provider.toPublicInfo),
         default: Provider.defaultModelIDs(providers),
-        connected: Object.keys(connected),
+        connected: Object.keys(providers).filter((id) => id in connected || credentials[id]),
       }
     })
 

+ 7 - 9
packages/opencode/src/session/session.ts

@@ -336,10 +336,8 @@ export function plan(input: { slug: string; time: { created: number } }, instanc
 }
 
 export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?: ProviderMetadata }) => {
-  const safe = (value: number) => {
-    if (!Number.isFinite(value)) return 0
-    return Math.max(0, value)
-  }
+  const finite = (value: number) => (Number.isFinite(value) ? value : 0)
+  const safe = (value: number) => Math.max(0, finite(value))
   const inputTokens = safe(input.usage.inputTokens ?? 0)
   const outputTokens = safe(input.usage.outputTokens ?? 0)
   const reasoningTokens = safe(input.usage.reasoningTokens ?? 0)
@@ -393,13 +391,13 @@ export const getUsage = (input: { model: Provider.Model; usage: Usage; metadata?
         ? new Decimal(totalNanoAiu).div(100_000_000_000).toNumber()
         : safe(
             new Decimal(0)
-              .add(new Decimal(tokens.input).mul(costInfo?.input ?? 0).div(1_000_000))
-              .add(new Decimal(tokens.output).mul(costInfo?.output ?? 0).div(1_000_000))
-              .add(new Decimal(tokens.cache.read).mul(costInfo?.cache?.read ?? 0).div(1_000_000))
-              .add(new Decimal(tokens.cache.write).mul(costInfo?.cache?.write ?? 0).div(1_000_000))
+              .add(new Decimal(tokens.input).mul(finite(costInfo?.input ?? 0)).div(1_000_000))
+              .add(new Decimal(tokens.output).mul(finite(costInfo?.output ?? 0)).div(1_000_000))
+              .add(new Decimal(tokens.cache.read).mul(finite(costInfo?.cache?.read ?? 0)).div(1_000_000))
+              .add(new Decimal(tokens.cache.write).mul(finite(costInfo?.cache?.write ?? 0)).div(1_000_000))
               // TODO: update models.dev to have better pricing model, for now:
               // charge reasoning tokens at the same rate as output tokens
-              .add(new Decimal(tokens.reasoning).mul(costInfo?.output ?? 0).div(1_000_000))
+              .add(new Decimal(tokens.reasoning).mul(finite(costInfo?.output ?? 0)).div(1_000_000))
               .toNumber(),
           ),
     tokens,

+ 16 - 0
packages/opencode/test/session/compaction.test.ts

@@ -1782,6 +1782,22 @@ describe("SessionNs.getUsage", () => {
     expect(Number.isNaN(result.cost)).toBe(false)
   })
 
+  test("ignores malformed cost fields", () => {
+    const model = createModel({
+      context: 100_000,
+      output: 32_000,
+      cost: { input: 3, output: 15, cache: { read: 0.3, write: 3.75 } },
+    })
+    Object.assign(model.cost, { input: {} })
+
+    const result = SessionNs.getUsage({
+      model,
+      usage: usage({ inputTokens: 1_000_000, outputTokens: 100_000, totalTokens: 1_100_000 }),
+    })
+
+    expect(result.cost).toBe(1.5)
+  })
+
   test("calculates cost correctly", () => {
     const model = createModel({
       context: 100_000,

+ 0 - 3
script/publish.ts

@@ -38,9 +38,6 @@ await prepareReleaseFiles()
 console.log("\n=== cli ===\n")
 await $`bun ./packages/opencode/script/publish.ts`
 
-console.log("\n=== preview cli ===\n")
-await $`bun ./packages/cli/script/publish.ts`
-
 console.log("\n=== sdk ===\n")
 await $`bun ./packages/sdk/js/script/publish.ts`