Kaynağa Gözat

fix issue with tool schemas and google

Dax Raad 9 ay önce
ebeveyn
işleme
ee91f31313

+ 3 - 0
bun.lock

@@ -86,6 +86,9 @@
     "sharp",
     "esbuild",
   ],
+  "patchedDependencies": {
+    "[email protected]": "patches/[email protected]",
+  },
   "overrides": {
     "zod": "3.24.2",
   },

+ 4 - 1
package.json

@@ -37,5 +37,8 @@
     "esbuild",
     "protobufjs",
     "sharp"
-  ]
+  ],
+  "patchedDependencies": {
+    "[email protected]": "patches/[email protected]"
+  }
 }

+ 5 - 2
packages/opencode/src/index.ts

@@ -93,8 +93,11 @@ const cli = yargs(hideBin(process.argv))
             if (Installation.VERSION === latest) return
             const method = await Installation.method()
             if (method === "unknown") return
-            await Installation.upgrade(method, latest).catch(() => {})
-            Bus.publish(Installation.Event.Updated, { version: latest })
+            await Installation.upgrade(method, latest)
+              .then(() => {
+                Bus.publish(Installation.Event.Updated, { version: latest })
+              })
+              .catch(() => {})
           })()
 
           await proc.exited

+ 1 - 1
packages/opencode/src/installation/index.ts

@@ -117,6 +117,6 @@ export namespace Installation {
   export async function latest() {
     return fetch("https://api.github.com/repos/sst/opencode/releases/latest")
       .then((res) => res.json())
-      .then((data) => data.tag_name.slice(1))
+      .then((data) => data.tag_name.slice(1) as string)
   }
 }

+ 43 - 2
packages/opencode/src/provider/provider.ts

@@ -23,6 +23,7 @@ import { ModelsDev } from "./models"
 import { NamedError } from "../util/error"
 import { Auth } from "../auth"
 import { TaskTool } from "../tool/task"
+import { GlobalConfig } from "../global/config"
 
 export namespace Provider {
   const log = Log.create({ service: "provider" })
@@ -257,7 +258,10 @@ export namespace Provider {
   }
 
   export async function defaultModel() {
-    const [provider] = await list().then((val) => Object.values(val))
+    const cfg = await GlobalConfig.get()
+    const provider = await list()
+      .then((val) => Object.values(val))
+      .then((x) => x.find((p) => !cfg.provider || cfg.provider === p.info.id))
     if (!provider) throw new Error("no providers found")
     const [model] = sort(Object.values(provider.info.models))
     if (!model) throw new Error("no models found")
@@ -285,11 +289,16 @@ export namespace Provider {
     TaskTool,
     TodoReadTool,
   ]
+
   const TOOL_MAPPING: Record<string, Tool.Info[]> = {
     anthropic: TOOLS.filter((t) => t.id !== "opencode.patch"),
-    openai: TOOLS,
+    openai: TOOLS.map((t) => ({
+      ...t,
+      parameters: optionalToNullable(t.parameters),
+    })),
     google: TOOLS,
   }
+
   export async function tools(providerID: string) {
     /*
     const cfg = await Config.get()
@@ -301,6 +310,38 @@ export namespace Provider {
     return TOOL_MAPPING[providerID] ?? TOOLS
   }
 
+  function optionalToNullable(schema: z.ZodTypeAny): z.ZodTypeAny {
+    if (schema instanceof z.ZodObject) {
+      const shape = schema.shape
+      const newShape: Record<string, z.ZodTypeAny> = {}
+
+      for (const [key, value] of Object.entries(shape)) {
+        const zodValue = value as z.ZodTypeAny
+        if (zodValue instanceof z.ZodOptional) {
+          newShape[key] = zodValue.unwrap().nullable()
+        } else {
+          newShape[key] = optionalToNullable(zodValue)
+        }
+      }
+
+      return z.object(newShape)
+    }
+
+    if (schema instanceof z.ZodArray) {
+      return z.array(optionalToNullable(schema.element))
+    }
+
+    if (schema instanceof z.ZodUnion) {
+      return z.union(
+        schema.options.map((option: z.ZodTypeAny) =>
+          optionalToNullable(option),
+        ) as [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]],
+      )
+    }
+
+    return schema
+  }
+
   export const ModelNotFoundError = NamedError.create(
     "ProviderModelNotFoundError",
     z.object({

+ 1 - 1
packages/opencode/src/session/index.ts

@@ -497,7 +497,7 @@ export namespace Session {
           msgs.map(toUIMessage).filter((x) => x.parts.length > 0),
         ),
       ],
-      temperature: model.info.id === "codex-mini-latest" ? undefined : 0,
+      temperature: model.info.temperature ? 0 : undefined,
       tools: {
         ...tools,
       },

+ 1 - 1
packages/opencode/src/tool/bash.ts

@@ -35,7 +35,7 @@ export const BashTool = Tool.define({
       .min(0)
       .max(MAX_TIMEOUT)
       .describe("Optional timeout in milliseconds")
-      .nullable(),
+      .optional(),
     description: z
       .string()
       .describe(

+ 1 - 1
packages/opencode/src/tool/edit.ts

@@ -21,7 +21,7 @@ export const EditTool = Tool.define({
       ),
     replaceAll: z
       .boolean()
-      .nullable()
+      .optional()
       .describe("Replace all occurences of old_string (default false)"),
   }),
   async execute(params, ctx) {

+ 1 - 1
packages/opencode/src/tool/glob.ts

@@ -11,7 +11,7 @@ export const GlobTool = Tool.define({
     pattern: z.string().describe("The glob pattern to match files against"),
     path: z
       .string()
-      .nullable()
+      .optional()
       .describe(
         `The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.`,
       ),

+ 2 - 2
packages/opencode/src/tool/grep.ts

@@ -14,13 +14,13 @@ export const GrepTool = Tool.define({
       .describe("The regex pattern to search for in file contents"),
     path: z
       .string()
-      .nullable()
+      .optional()
       .describe(
         "The directory to search in. Defaults to the current working directory.",
       ),
     include: z
       .string()
-      .nullable()
+      .optional()
       .describe(
         'File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")',
       ),

+ 2 - 2
packages/opencode/src/tool/ls.ts

@@ -29,11 +29,11 @@ export const ListTool = Tool.define({
       .describe(
         "The absolute path to the directory to list (must be absolute, not relative)",
       )
-      .nullable(),
+      .optional(),
     ignore: z
       .array(z.string())
       .describe("List of glob patterns to ignore")
-      .nullable(),
+      .optional(),
   }),
   async execute(params) {
     const app = App.info()

+ 2 - 2
packages/opencode/src/tool/read.ts

@@ -19,11 +19,11 @@ export const ReadTool = Tool.define({
     offset: z
       .number()
       .describe("The line number to start reading from (0-based)")
-      .nullable(),
+      .optional(),
     limit: z
       .number()
       .describe("The number of lines to read (defaults to 2000)")
-      .nullable(),
+      .optional(),
   }),
   async execute(params, ctx) {
     let filePath = params.filePath

+ 1 - 1
packages/opencode/src/tool/webfetch.ts

@@ -22,7 +22,7 @@ export const WebFetchTool = Tool.define({
       .min(0)
       .max(MAX_TIMEOUT / 1000)
       .describe("Optional timeout in seconds (max 120)")
-      .nullable(),
+      .optional(),
   }),
   async execute(params, ctx) {
     // Validate URL

+ 2 - 2
packages/opencode/test/tool/tool.test.ts

@@ -9,7 +9,7 @@ describe("tool.glob", () => {
       let result = await GlobTool.execute(
         {
           pattern: "./node_modules/**/*",
-          path: null,
+          path: undefined,
         },
         {
           sessionID: "test",
@@ -25,7 +25,7 @@ describe("tool.glob", () => {
       let result = await GlobTool.execute(
         {
           pattern: "*.json",
-          path: null,
+          path: undefined,
         },
         {
           sessionID: "test",

+ 13 - 0
patches/[email protected]

@@ -0,0 +1,13 @@
+diff --git a/dist/index.mjs b/dist/index.mjs
+index 92a80377692488c4ba8801ce33e7736ad7055e43..add6281bbecaa1c03d3b48eb99aead4a7a7336b2 100644
+--- a/dist/index.mjs
++++ b/dist/index.mjs
+@@ -1593,7 +1593,7 @@ function prepareCallSettings({
+   return {
+     maxTokens,
+     // TODO v5 remove default 0 for temperature
+-    temperature: temperature != null ? temperature : 0,
++    temperature: temperature,
+     topP,
+     topK,
+     presencePenalty,