From 03a97a64a30740e9c6abc4e05e588fc353d7f9e8 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Sun, 26 Apr 2026 20:35:44 -0400 Subject: [PATCH] chore(llm): fix low-hanging lint warnings --- packages/llm/src/llm.ts | 15 ++++++++++----- .../llm/src/provider/anthropic-messages.ts | 3 +-- packages/llm/src/provider/gemini.ts | 2 +- packages/llm/src/provider/openai-chat.ts | 3 +-- packages/llm/src/provider/openai-responses.ts | 4 ++-- packages/llm/src/provider/shared.ts | 11 ++++++++++- packages/llm/src/tool-runtime.ts | 19 +++++++++++++++++-- 7 files changed, 42 insertions(+), 15 deletions(-) diff --git a/packages/llm/src/llm.ts b/packages/llm/src/llm.ts index 3b283d5cbe..8ade8a0d0d 100644 --- a/packages/llm/src/llm.ts +++ b/packages/llm/src/llm.ts @@ -12,7 +12,6 @@ import { ToolChoice, ToolDefinition, type ContentPart, - type Protocol, type ModelID as ModelIDType, type ProviderID as ProviderIDType, type ReasoningEffort, @@ -50,7 +49,7 @@ export type ToolChoiceInput = | string export type ToolResultInput = Omit & { - readonly result: ToolResultValue | unknown + readonly result: unknown readonly resultType?: ToolResultValue["type"] } @@ -106,7 +105,7 @@ export const model = (input: ModelInput) => { ...rest, id: ModelID.make(input.id), provider: ProviderID.make(input.provider), - protocol: input.protocol as Protocol, + protocol: input.protocol, capabilities: modelCapabilities instanceof ModelCapabilities ? modelCapabilities : capabilities(modelCapabilities), limits: modelLimits instanceof ModelLimits ? modelLimits : limits(modelLimits), }) @@ -119,8 +118,14 @@ export const tool = (input: ToolDefinition | ConstructorParameters): ToolCallPart => ({ type: "tool-call", ...input }) -const toolResultValue = (value: ToolResultValue | unknown, type: ToolResultValue["type"] = "json"): ToolResultValue => { - if (typeof value === "object" && value !== null && "type" in value && "value" in value) return value as ToolResultValue +const isRecord = (value: unknown): value is Record => + typeof value === "object" && value !== null && !Array.isArray(value) + +const isToolResultValue = (value: unknown): value is ToolResultValue => + isRecord(value) && (value.type === "text" || value.type === "json" || value.type === "error") && "value" in value + +const toolResultValue = (value: unknown, type: ToolResultValue["type"] = "json"): ToolResultValue => { + if (isToolResultValue(value)) return value return { type, value } } diff --git a/packages/llm/src/provider/anthropic-messages.ts b/packages/llm/src/provider/anthropic-messages.ts index e2888d35da..9589ca27c2 100644 --- a/packages/llm/src/provider/anthropic-messages.ts +++ b/packages/llm/src/provider/anthropic-messages.ts @@ -1,4 +1,4 @@ -import { Effect, Schema, Stream } from "effect" +import { Effect, Schema } from "effect" import type { HttpClientResponse } from "effect/unstable/http" import { Adapter } from "../adapter" import { capabilities, model as llmModel, type ModelInput } from "../llm" @@ -8,7 +8,6 @@ import { type FinishReason, type LLMEvent, type LLMRequest, - type TextPart, type ToolCallPart, type ToolDefinition, type ToolResultPart, diff --git a/packages/llm/src/provider/gemini.ts b/packages/llm/src/provider/gemini.ts index 486b4b897c..96ec060c5f 100644 --- a/packages/llm/src/provider/gemini.ts +++ b/packages/llm/src/provider/gemini.ts @@ -1,4 +1,4 @@ -import { Effect, Schema, Stream } from "effect" +import { Effect, Schema } from "effect" import type { HttpClientResponse } from "effect/unstable/http" import { Adapter } from "../adapter" import { capabilities, model as llmModel, type ModelInput } from "../llm" diff --git a/packages/llm/src/provider/openai-chat.ts b/packages/llm/src/provider/openai-chat.ts index 3f5a4bfb7e..c6f33be090 100644 --- a/packages/llm/src/provider/openai-chat.ts +++ b/packages/llm/src/provider/openai-chat.ts @@ -1,11 +1,10 @@ -import { Effect, Schema, Stream } from "effect" +import { Effect, Schema } from "effect" import type { HttpClientResponse } from "effect/unstable/http" import { Adapter } from "../adapter" import { capabilities, model as llmModel, type ModelInput } from "../llm" import { Usage, type FinishReason, - type ContentPart, type LLMEvent, type LLMRequest, type TextPart, diff --git a/packages/llm/src/provider/openai-responses.ts b/packages/llm/src/provider/openai-responses.ts index a901c96b40..f8c7069e13 100644 --- a/packages/llm/src/provider/openai-responses.ts +++ b/packages/llm/src/provider/openai-responses.ts @@ -1,4 +1,4 @@ -import { Effect, Schema, Stream } from "effect" +import { Effect, Schema } from "effect" import type { HttpClientResponse } from "effect/unstable/http" import { Adapter } from "../adapter" import { capabilities, model as llmModel, type ModelInput } from "../llm" @@ -322,7 +322,7 @@ const hostedToolResult = (item: OpenAIResponsesStreamItem) => { } const hostedToolEvents = (item: OpenAIResponsesStreamItem & { id: string }): ReadonlyArray => { - const name = HOSTED_TOOL_NAMES[item.type]! + const name = HOSTED_TOOL_NAMES[item.type] return [ { type: "tool-call", id: item.id, name, input: hostedToolInput(item), providerExecuted: true }, { type: "tool-result", id: item.id, name, result: hostedToolResult(item), providerExecuted: true }, diff --git a/packages/llm/src/provider/shared.ts b/packages/llm/src/provider/shared.ts index 88f9b4f0bc..b9e59f500b 100644 --- a/packages/llm/src/provider/shared.ts +++ b/packages/llm/src/provider/shared.ts @@ -51,6 +51,15 @@ export const toolResultText = (part: ToolResultPart) => { return encodeJson(part.result.value) } +const errorText = (error: unknown) => { + if (error instanceof Error) return error.message + if (typeof error === "string") return error + if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") return String(error) + if (error === null) return "null" + if (error === undefined) return "undefined" + return "Unknown stream error" +} + const streamError = (adapter: string, message: string, cause: Cause.Cause) => { const failed = cause.reasons.find(Cause.isFailReason)?.error if (failed instanceof ProviderChunkError) return failed @@ -85,7 +94,7 @@ export const framed = (input: { readonly onHalt?: (state: State) => ReadonlyArray }): Stream.Stream => { const bytes = input.response.stream.pipe( - Stream.mapError((error) => chunkError(input.adapter, input.readError, String(error))), + Stream.mapError((error) => chunkError(input.adapter, input.readError, errorText(error))), ) return input.framing(bytes).pipe( Stream.mapEffect(input.decodeChunk), diff --git a/packages/llm/src/tool-runtime.ts b/packages/llm/src/tool-runtime.ts index ca5f59a5eb..8d1b784e12 100644 --- a/packages/llm/src/tool-runtime.ts +++ b/packages/llm/src/tool-runtime.ts @@ -43,6 +43,21 @@ export interface RunOptions { readonly stopWhen?: (state: RuntimeState) => boolean } +const requestInput = (request: LLMRequest): ConstructorParameters[0] => ({ + id: request.id, + model: request.model, + system: request.system, + messages: request.messages, + tools: request.tools, + toolChoice: request.toolChoice, + generation: request.generation, + reasoning: request.reasoning, + cache: request.cache, + responseFormat: request.responseFormat, + metadata: request.metadata, + native: request.native, +}) + /** * Run a model with a typed tool record. The runtime streams the model, on * each `tool-call` event decodes the input against the tool's `parameters` @@ -64,7 +79,7 @@ export const run = ( const tools = options.tools as Tools const runtimeTools = toDefinitions(tools) const initialRequest = new LLMRequest({ - ...options.request, + ...requestInput(options.request), tools: [ ...options.request.tools.filter((tool) => !runtimeTools.some((runtimeTool) => runtimeTool.name === tool.name)), ...runtimeTools, @@ -92,7 +107,7 @@ export const run = ( { concurrency }, ) const followUp = new LLMRequest({ - ...request, + ...requestInput(request), messages: [ ...request.messages, LLM.assistant(state.assistantContent),