mirror of
https://github.com/sst/opencode.git
synced 2025-08-22 14:04:07 +00:00
Merge 9c247a72e3
into ba2e86c7ef
This commit is contained in:
commit
c69f200452
5 changed files with 453 additions and 88 deletions
11
README.md
11
README.md
|
@ -48,6 +48,17 @@ OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bas
|
||||||
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Morph Fast Apply (Optional)
|
||||||
|
|
||||||
|
For faster, more accurate code edits, opencode can integrate with [Morph](https://morphllm.com) - an AI model specialized for code merging at 4500+ tokens/second with 98.8% accuracy.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable Morph Fast Apply
|
||||||
|
export MORPH_API_KEY=your_api_key
|
||||||
|
```
|
||||||
|
|
||||||
|
When enabled, opencode's edit tool automatically uses Morph's intelligent code merging instead of search-and-replace, supporting multiple edits and `// ... existing code ...` syntax.
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
For more info on how to configure opencode [**head over to our docs**](https://opencode.ai/docs).
|
For more info on how to configure opencode [**head over to our docs**](https://opencode.ai/docs).
|
||||||
|
|
|
@ -9,7 +9,10 @@ import { Tool } from "./tool"
|
||||||
import { LSP } from "../lsp"
|
import { LSP } from "../lsp"
|
||||||
import { createTwoFilesPatch } from "diff"
|
import { createTwoFilesPatch } from "diff"
|
||||||
import { Permission } from "../permission"
|
import { Permission } from "../permission"
|
||||||
|
// @ts-ignore
|
||||||
import DESCRIPTION from "./edit.txt"
|
import DESCRIPTION from "./edit.txt"
|
||||||
|
// @ts-ignore
|
||||||
|
import MORPH_DESCRIPTION from "./edit-morph.txt"
|
||||||
import { App } from "../app/app"
|
import { App } from "../app/app"
|
||||||
import { File } from "../file"
|
import { File } from "../file"
|
||||||
import { Bus } from "../bus"
|
import { Bus } from "../bus"
|
||||||
|
@ -17,15 +20,144 @@ import { FileTime } from "../file/time"
|
||||||
import { Filesystem } from "../util/filesystem"
|
import { Filesystem } from "../util/filesystem"
|
||||||
import { Agent } from "../agent/agent"
|
import { Agent } from "../agent/agent"
|
||||||
|
|
||||||
export const EditTool = Tool.define("edit", {
|
// OpenAI-compatible client for Morph API
|
||||||
description: DESCRIPTION,
|
class MorphClient {
|
||||||
parameters: z.object({
|
private apiKey: string
|
||||||
filePath: z.string().describe("The absolute path to the file to modify"),
|
private baseURL = "https://api.morphllm.com/v1"
|
||||||
oldString: z.string().describe("The text to replace"),
|
|
||||||
newString: z.string().describe("The text to replace it with (must be different from oldString)"),
|
constructor(apiKey: string) {
|
||||||
replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"),
|
this.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
async apply(instruction: string, initialCode: string, codeEdit: string): Promise<string> {
|
||||||
|
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${this.apiKey}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: "morph-v3-large",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `<instruction>${instruction}</instruction>\n<code>${initialCode}</code>\n<update>${codeEdit}</update>`,
|
||||||
|
},
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
async execute(params, ctx) {
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text()
|
||||||
|
throw new Error(`Morph API error: ${response.status} ${response.statusText}\n${errorText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
return result.choices[0].message.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeMorphEdit(
|
||||||
|
params: { target_file: string; instructions: string; code_edit: string },
|
||||||
|
ctx: any,
|
||||||
|
morphApiKey: string
|
||||||
|
) {
|
||||||
|
if (!params.target_file) {
|
||||||
|
throw new Error("target_file is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.instructions) {
|
||||||
|
throw new Error("instructions is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.code_edit) {
|
||||||
|
throw new Error("code_edit is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = App.info()
|
||||||
|
const filePath = path.isAbsolute(params.target_file) ? params.target_file : path.join(app.path.cwd, params.target_file)
|
||||||
|
|
||||||
|
if (!Filesystem.contains(app.path.cwd, filePath)) {
|
||||||
|
throw new Error(`File ${filePath} is not in the current working directory`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = await Agent.get(ctx.agent)
|
||||||
|
|
||||||
|
// Read the existing file
|
||||||
|
const file = Bun.file(filePath)
|
||||||
|
const stats = await file.stat().catch(() => {})
|
||||||
|
if (!stats) throw new Error(`File ${filePath} not found`)
|
||||||
|
if (stats.isDirectory()) throw new Error(`Path is a directory, not a file: ${filePath}`)
|
||||||
|
|
||||||
|
await FileTime.assert(ctx.sessionID, filePath)
|
||||||
|
const initialCode = await file.text()
|
||||||
|
|
||||||
|
// Use Morph API to apply the edit
|
||||||
|
const morphClient = new MorphClient(morphApiKey)
|
||||||
|
let mergedCode: string
|
||||||
|
|
||||||
|
try {
|
||||||
|
mergedCode = await morphClient.apply(params.instructions, initialCode, params.code_edit)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to apply edit with Morph: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff = trimDiff(createTwoFilesPatch(filePath, filePath, initialCode, mergedCode))
|
||||||
|
|
||||||
|
// Check permissions if needed
|
||||||
|
if (agent.permission.edit === "ask") {
|
||||||
|
await Permission.ask({
|
||||||
|
type: "edit",
|
||||||
|
sessionID: ctx.sessionID,
|
||||||
|
messageID: ctx.messageID,
|
||||||
|
callID: ctx.callID,
|
||||||
|
title: "🚀 Morph Fast Apply: " + filePath,
|
||||||
|
metadata: {
|
||||||
|
filePath,
|
||||||
|
diff,
|
||||||
|
morphApplied: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the merged code to file
|
||||||
|
await Bun.write(filePath, mergedCode)
|
||||||
|
await Bus.publish(File.Event.Edited, {
|
||||||
|
file: filePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
FileTime.read(ctx.sessionID, filePath)
|
||||||
|
|
||||||
|
let output = ""
|
||||||
|
await LSP.touchFile(filePath, true)
|
||||||
|
const diagnostics = await LSP.diagnostics()
|
||||||
|
for (const [file, issues] of Object.entries(diagnostics)) {
|
||||||
|
if (issues.length === 0) continue
|
||||||
|
if (file === filePath) {
|
||||||
|
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
output += `\n<project_diagnostics>\n${file}\n${issues
|
||||||
|
.filter((item) => item.severity === 1)
|
||||||
|
.map(LSP.Diagnostic.pretty)
|
||||||
|
.join("\n")}\n</project_diagnostics>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
diagnostics,
|
||||||
|
diff,
|
||||||
|
morphApplied: true,
|
||||||
|
},
|
||||||
|
title: `🚀 ${path.relative(app.path.root, filePath)}`,
|
||||||
|
output: output || `Successfully applied edit using Morph Fast Apply:\n\n${diff}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeRegularEdit(
|
||||||
|
params: { filePath: string; oldString: string; newString: string; replaceAll?: boolean },
|
||||||
|
ctx: any
|
||||||
|
) {
|
||||||
if (!params.filePath) {
|
if (!params.filePath) {
|
||||||
throw new Error("filePath is required")
|
throw new Error("filePath is required")
|
||||||
}
|
}
|
||||||
|
@ -126,8 +258,40 @@ export const EditTool = Tool.define("edit", {
|
||||||
title: `${path.relative(app.path.root, filePath)}`,
|
title: `${path.relative(app.path.root, filePath)}`,
|
||||||
output,
|
output,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
})
|
|
||||||
|
export const EditTool = Tool.define("edit", (async () => {
|
||||||
|
const morphApiKey = process.env['MORPH_API_KEY']
|
||||||
|
|
||||||
|
if (morphApiKey) {
|
||||||
|
// Morph Fast Apply mode
|
||||||
|
return {
|
||||||
|
description: MORPH_DESCRIPTION as string,
|
||||||
|
parameters: z.object({
|
||||||
|
target_file: z.string().describe("The target file to modify"),
|
||||||
|
instructions: z.string().describe("A single sentence written in the first person describing what you're changing. Used to help disambiguate uncertainty in the edit."),
|
||||||
|
code_edit: z.string().describe("Specify ONLY the precise lines of code that you wish to edit. Use `// ... existing code ...` for unchanged sections."),
|
||||||
|
}),
|
||||||
|
async execute(params: any, ctx: any) {
|
||||||
|
return executeMorphEdit(params, ctx, morphApiKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Regular search-and-replace mode
|
||||||
|
return {
|
||||||
|
description: DESCRIPTION as string,
|
||||||
|
parameters: z.object({
|
||||||
|
filePath: z.string().describe("The absolute path to the file to modify"),
|
||||||
|
oldString: z.string().describe("The text to replace"),
|
||||||
|
newString: z.string().describe("The text to replace it with (must be different from oldString)"),
|
||||||
|
replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"),
|
||||||
|
}),
|
||||||
|
async execute(params: any, ctx: any) {
|
||||||
|
return executeRegularEdit(params, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) as any)
|
||||||
|
|
||||||
export type Replacer = (content: string, find: string) => Generator<string, void, unknown>
|
export type Replacer = (content: string, find: string) => Generator<string, void, unknown>
|
||||||
|
|
||||||
|
@ -606,7 +770,8 @@ export function replace(content: string, oldString: string, newString: string, r
|
||||||
// ContextAwareReplacer,
|
// ContextAwareReplacer,
|
||||||
// MultiOccurrenceReplacer,
|
// MultiOccurrenceReplacer,
|
||||||
]) {
|
]) {
|
||||||
for (const search of replacer(content, oldString)) {
|
const searches = Array.from(replacer(content, oldString))
|
||||||
|
for (const search of searches) {
|
||||||
const index = content.indexOf(search)
|
const index = content.indexOf(search)
|
||||||
if (index === -1) continue
|
if (index === -1) continue
|
||||||
if (replaceAll) {
|
if (replaceAll) {
|
||||||
|
|
164
packages/opencode/src/tool/morphedit.ts
Normal file
164
packages/opencode/src/tool/morphedit.ts
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
import { z } from "zod"
|
||||||
|
import * as path from "path"
|
||||||
|
import { Tool } from "./tool"
|
||||||
|
import { createTwoFilesPatch } from "diff"
|
||||||
|
import { Permission } from "../permission"
|
||||||
|
import DESCRIPTION from "./morphedit.txt"
|
||||||
|
import { App } from "../app/app"
|
||||||
|
import { File } from "../file"
|
||||||
|
import { Bus } from "../bus"
|
||||||
|
import { FileTime } from "../file/time"
|
||||||
|
import { Filesystem } from "../util/filesystem"
|
||||||
|
import { Agent } from "../agent/agent"
|
||||||
|
import { LSP } from "../lsp"
|
||||||
|
|
||||||
|
// OpenAI-compatible client for Morph API
|
||||||
|
class MorphClient {
|
||||||
|
private apiKey: string
|
||||||
|
private baseURL = "https://api.morphllm.com/v1"
|
||||||
|
|
||||||
|
constructor(apiKey: string) {
|
||||||
|
this.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
async apply(instruction: string, initialCode: string, codeEdit: string): Promise<string> {
|
||||||
|
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${this.apiKey}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model: "morph-v3-large",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: `<instruction>${instruction}</instruction>\n<code>${initialCode}</code>\n<update>${codeEdit}</update>`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text()
|
||||||
|
throw new Error(`Morph API error: ${response.status} ${response.statusText}\n${errorText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json()
|
||||||
|
return result.choices[0].message.content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function trimDiff(diff: string): string {
|
||||||
|
return diff
|
||||||
|
.split("\n")
|
||||||
|
.slice(4) // Remove the header lines
|
||||||
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MorphEditTool = Tool.define("edit", {
|
||||||
|
description: DESCRIPTION,
|
||||||
|
parameters: z.object({
|
||||||
|
target_file: z.string().describe("The target file to modify"),
|
||||||
|
instructions: z.string().describe("A single sentence written in the first person describing what you're changing. Used to help disambiguate uncertainty in the edit."),
|
||||||
|
code_edit: z.string().describe("Specify ONLY the precise lines of code that you wish to edit. Use `// ... existing code ...` for unchanged sections."),
|
||||||
|
}),
|
||||||
|
async execute(params, ctx) {
|
||||||
|
if (!params.target_file) {
|
||||||
|
throw new Error("target_file is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.instructions) {
|
||||||
|
throw new Error("instructions is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.code_edit) {
|
||||||
|
throw new Error("code_edit is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for Morph API key
|
||||||
|
const morphApiKey = process.env['MORPH_API_KEY']
|
||||||
|
if (!morphApiKey) {
|
||||||
|
throw new Error("MORPH_API_KEY environment variable is required for morphedit tool")
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = App.info()
|
||||||
|
const filePath = path.isAbsolute(params.target_file) ? params.target_file : path.join(app.path.cwd, params.target_file)
|
||||||
|
|
||||||
|
if (!Filesystem.contains(app.path.cwd, filePath)) {
|
||||||
|
throw new Error(`File ${filePath} is not in the current working directory`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const agent = await Agent.get(ctx.agent)
|
||||||
|
|
||||||
|
// Read the existing file
|
||||||
|
const file = Bun.file(filePath)
|
||||||
|
const stats = await file.stat().catch(() => {})
|
||||||
|
if (!stats) throw new Error(`File ${filePath} not found`)
|
||||||
|
if (stats.isDirectory()) throw new Error(`Path is a directory, not a file: ${filePath}`)
|
||||||
|
|
||||||
|
await FileTime.assert(ctx.sessionID, filePath)
|
||||||
|
const initialCode = await file.text()
|
||||||
|
|
||||||
|
// Use Morph API to apply the edit
|
||||||
|
const morphClient = new MorphClient(morphApiKey)
|
||||||
|
let mergedCode: string
|
||||||
|
|
||||||
|
try {
|
||||||
|
mergedCode = await morphClient.apply(params.instructions, initialCode, params.code_edit)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Failed to apply edit with Morph: ${error instanceof Error ? error.message : String(error)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const diff = trimDiff(createTwoFilesPatch(filePath, filePath, initialCode, mergedCode))
|
||||||
|
|
||||||
|
// Check permissions if needed
|
||||||
|
if (agent.permission.edit === "ask") {
|
||||||
|
await Permission.ask({
|
||||||
|
type: "edit",
|
||||||
|
sessionID: ctx.sessionID,
|
||||||
|
messageID: ctx.messageID,
|
||||||
|
callID: ctx.callID,
|
||||||
|
title: "🚀 Morph Fast Apply: " + filePath,
|
||||||
|
metadata: {
|
||||||
|
filePath,
|
||||||
|
diff,
|
||||||
|
morphApplied: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the merged code to file
|
||||||
|
await Bun.write(filePath, mergedCode)
|
||||||
|
await Bus.publish(File.Event.Edited, {
|
||||||
|
file: filePath,
|
||||||
|
})
|
||||||
|
|
||||||
|
FileTime.read(ctx.sessionID, filePath)
|
||||||
|
|
||||||
|
let output = ""
|
||||||
|
await LSP.touchFile(filePath, true)
|
||||||
|
const diagnostics = await LSP.diagnostics()
|
||||||
|
for (const [file, issues] of Object.entries(diagnostics)) {
|
||||||
|
if (issues.length === 0) continue
|
||||||
|
if (file === filePath) {
|
||||||
|
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
output += `\n<project_diagnostics>\n${file}\n${issues
|
||||||
|
.filter((item) => item.severity === 1)
|
||||||
|
.map(LSP.Diagnostic.pretty)
|
||||||
|
.join("\n")}\n</project_diagnostics>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
metadata: {
|
||||||
|
diagnostics,
|
||||||
|
diff,
|
||||||
|
morphApplied: true,
|
||||||
|
},
|
||||||
|
title: `🚀 ${path.relative(app.path.root, filePath)}`,
|
||||||
|
output: output || `Successfully applied edit using Morph Fast Apply:\n\n${diff}`,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
21
packages/opencode/src/tool/morphedit.txt
Normal file
21
packages/opencode/src/tool/morphedit.txt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MORPH FAST APPLY - Use this tool to make an edit to an existing file.
|
||||||
|
|
||||||
|
This will be read by a less intelligent model, which will quickly apply the edit. You should make it clear what the edit is, while also minimizing the unchanged code you write.
|
||||||
|
When writing the edit, you should specify each edit in sequence, with the special comment // ... existing code ... to represent unchanged code in between edited lines.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
// ... existing code ...
|
||||||
|
FIRST_EDIT
|
||||||
|
// ... existing code ...
|
||||||
|
SECOND_EDIT
|
||||||
|
// ... existing code ...
|
||||||
|
THIRD_EDIT
|
||||||
|
// ... existing code ...
|
||||||
|
|
||||||
|
You should still bias towards repeating as few lines of the original file as possible to convey the change.
|
||||||
|
But, each edit should contain sufficient context of unchanged lines around the code you're editing to resolve ambiguity.
|
||||||
|
DO NOT omit spans of pre-existing code (or comments) without using the // ... existing code ... comment to indicate its absence. If you omit the existing code comment, the model may inadvertently delete these lines.
|
||||||
|
If you plan on deleting a section, you must provide context before and after to delete it. If the initial code is ```code \n Block 1 \n Block 2 \n Block 3 \n code```, and you want to remove Block 2, you would output ```// ... existing code ... \n Block 1 \n Block 3 \n // ... existing code ...```.
|
||||||
|
Make sure it is clear what the edit should be, and where it should be applied.
|
||||||
|
Make edits to a file in a single edit_file instead of multiple edit_file calls to the same file. The apply model can handle many distinct edits at once.
|
|
@ -1,6 +1,7 @@
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { BashTool } from "./bash"
|
import { BashTool } from "./bash"
|
||||||
import { EditTool } from "./edit"
|
import { EditTool } from "./edit"
|
||||||
|
import { MorphEditTool } from "./morphedit"
|
||||||
import { GlobTool } from "./glob"
|
import { GlobTool } from "./glob"
|
||||||
import { GrepTool } from "./grep"
|
import { GrepTool } from "./grep"
|
||||||
import { ListTool } from "./ls"
|
import { ListTool } from "./ls"
|
||||||
|
@ -14,10 +15,13 @@ import { InvalidTool } from "./invalid"
|
||||||
import type { Agent } from "../agent/agent"
|
import type { Agent } from "../agent/agent"
|
||||||
|
|
||||||
export namespace ToolRegistry {
|
export namespace ToolRegistry {
|
||||||
|
// Use Morph Fast Apply as the default edit tool if MORPH_API_KEY is present
|
||||||
|
const DefaultEditTool = process.env['MORPH_API_KEY'] ? MorphEditTool : EditTool
|
||||||
|
|
||||||
const ALL = [
|
const ALL = [
|
||||||
InvalidTool,
|
InvalidTool,
|
||||||
BashTool,
|
BashTool,
|
||||||
EditTool,
|
DefaultEditTool,
|
||||||
WebFetchTool,
|
WebFetchTool,
|
||||||
GlobTool,
|
GlobTool,
|
||||||
GrepTool,
|
GrepTool,
|
||||||
|
@ -45,21 +49,21 @@ export namespace ToolRegistry {
|
||||||
if (providerID === "openai") {
|
if (providerID === "openai") {
|
||||||
return result.map((t) => ({
|
return result.map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
parameters: optionalToNullable(t.parameters),
|
parameters: optionalToNullable(t.parameters as z.ZodTypeAny),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (providerID === "azure") {
|
if (providerID === "azure") {
|
||||||
return result.map((t) => ({
|
return result.map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
parameters: optionalToNullable(t.parameters),
|
parameters: optionalToNullable(t.parameters as z.ZodTypeAny),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (providerID === "google") {
|
if (providerID === "google") {
|
||||||
return result.map((t) => ({
|
return result.map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
parameters: sanitizeGeminiParameters(t.parameters),
|
parameters: sanitizeGeminiParameters(t.parameters as z.ZodTypeAny),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue