feat(core): add embedded v2 session runtime and tool foundation (#30632)
This commit is contained in:
parent
c35267776a
commit
76ee87ead8
215 changed files with 31398 additions and 3332 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { Effect, JsonSchema, Schema } from "effect"
|
||||
import type { ToolCallPart, ToolDefinition as ToolDefinitionClass } from "./schema"
|
||||
import { ToolDefinition, ToolFailure } from "./schema"
|
||||
import type { ToolCallPart, ToolContent, ToolDefinition as ToolDefinitionClass, ToolOutput as ToolOutputType } from "./schema"
|
||||
import { ToolDefinition, ToolFailure, ToolOutput, toolText } from "./schema"
|
||||
|
||||
/**
|
||||
* Schema constraint for tool parameters / success values: no decoding or
|
||||
|
|
@ -18,6 +18,16 @@ export type ToolExecute<Parameters extends ToolSchema<any>, Success extends Tool
|
|||
context?: ToolExecuteContext,
|
||||
) => Effect.Effect<Schema.Schema.Type<Success>, ToolFailure>
|
||||
|
||||
export interface ToolModelOutputInput<Parameters, Output> {
|
||||
readonly callID: ToolCallPart["id"]
|
||||
readonly parameters: Parameters
|
||||
readonly output: Output
|
||||
}
|
||||
|
||||
export type ToolToModelOutput<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> = (
|
||||
input: ToolModelOutputInput<Schema.Schema.Type<Parameters>, Success["Encoded"]>,
|
||||
) => ReadonlyArray<ToolContent>
|
||||
|
||||
/**
|
||||
* A type-safe LLM tool. Each tool bundles its own description, parameter
|
||||
* Schema and success Schema. The execute handler is optional: omit it when you
|
||||
|
|
@ -28,22 +38,27 @@ export type ToolExecute<Parameters extends ToolSchema<any>, Success extends Tool
|
|||
* the stream.
|
||||
*
|
||||
* Internally each tool also carries memoized codecs and a precomputed
|
||||
* `ToolDefinition` so the runtime doesn't rebuild them per invocation.
|
||||
* `ToolDefinition` so callers do not rebuild them per invocation.
|
||||
*/
|
||||
export interface Tool<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> {
|
||||
readonly description: string
|
||||
readonly parameters: Parameters
|
||||
readonly success: Success
|
||||
readonly execute?: ToolExecute<Parameters, Success>
|
||||
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>
|
||||
/** @internal */
|
||||
readonly _decode: (input: unknown) => Effect.Effect<Schema.Schema.Type<Parameters>, Schema.SchemaError>
|
||||
/** @internal */
|
||||
readonly _encode: (value: Schema.Schema.Type<Success>) => Effect.Effect<unknown, Schema.SchemaError>
|
||||
/** @internal */
|
||||
readonly _project: (parameters: Schema.Schema.Type<Parameters>, callID: ToolCallPart["id"], output: unknown) => ToolOutputType
|
||||
/** @internal */
|
||||
readonly _legacyResult: boolean
|
||||
/** @internal */
|
||||
readonly _definition: ToolDefinitionClass
|
||||
}
|
||||
|
||||
export type AnyTool = Tool<ToolSchema<any>, ToolSchema<any>>
|
||||
export type AnyTool = Tool<any, any>
|
||||
|
||||
export type ExecutableTool<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>> = Tool<
|
||||
Parameters,
|
||||
|
|
@ -52,7 +67,7 @@ export type ExecutableTool<Parameters extends ToolSchema<any>, Success extends T
|
|||
readonly execute: ToolExecute<Parameters, Success>
|
||||
}
|
||||
|
||||
export type AnyExecutableTool = ExecutableTool<ToolSchema<any>, ToolSchema<any>>
|
||||
export type AnyExecutableTool = ExecutableTool<any, any>
|
||||
|
||||
export type ExecutableTools = Record<string, AnyExecutableTool>
|
||||
|
||||
|
|
@ -61,12 +76,15 @@ type TypedToolConfig = {
|
|||
readonly parameters: ToolSchema<any>
|
||||
readonly success: ToolSchema<any>
|
||||
readonly execute?: ToolExecute<ToolSchema<any>, ToolSchema<any>>
|
||||
readonly toModelOutput?: ToolToModelOutput<ToolSchema<any>, ToolSchema<any>>
|
||||
}
|
||||
|
||||
type DynamicToolConfig = {
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
readonly execute?: (params: unknown, context?: ToolExecuteContext) => Effect.Effect<unknown, ToolFailure>
|
||||
readonly toModelOutput?: (input: ToolModelOutputInput<unknown, unknown>) => ReadonlyArray<ToolContent>
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,30 +115,36 @@ type DynamicToolConfig = {
|
|||
* })
|
||||
* ```
|
||||
*
|
||||
* In both modes the produced tool flows through `toDefinitions(...)` and the
|
||||
* runtime identically.
|
||||
* In both modes the produced tool flows through `toDefinitions(...)`
|
||||
* identically.
|
||||
*/
|
||||
export function make<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>>(config: {
|
||||
readonly description: string
|
||||
readonly parameters: Parameters
|
||||
readonly success: Success
|
||||
readonly execute: ToolExecute<Parameters, Success>
|
||||
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>
|
||||
}): ExecutableTool<Parameters, Success>
|
||||
export function make<Parameters extends ToolSchema<any>, Success extends ToolSchema<any>>(config: {
|
||||
readonly description: string
|
||||
readonly parameters: Parameters
|
||||
readonly success: Success
|
||||
readonly execute?: undefined
|
||||
readonly toModelOutput?: ToolToModelOutput<Parameters, Success>
|
||||
}): Tool<Parameters, Success>
|
||||
export function make(config: {
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
readonly execute: (params: unknown, context?: ToolExecuteContext) => Effect.Effect<unknown, ToolFailure>
|
||||
readonly toModelOutput?: (input: ToolModelOutputInput<unknown, unknown>) => ReadonlyArray<ToolContent>
|
||||
}): AnyExecutableTool
|
||||
export function make(config: {
|
||||
readonly description: string
|
||||
readonly jsonSchema: JsonSchema.JsonSchema
|
||||
readonly outputSchema?: JsonSchema.JsonSchema
|
||||
readonly execute?: undefined
|
||||
readonly toModelOutput?: (input: ToolModelOutputInput<unknown, unknown>) => ReadonlyArray<ToolContent>
|
||||
}): AnyTool
|
||||
export function make(config: TypedToolConfig | DynamicToolConfig): AnyTool {
|
||||
if ("jsonSchema" in config) {
|
||||
|
|
@ -129,12 +153,16 @@ export function make(config: TypedToolConfig | DynamicToolConfig): AnyTool {
|
|||
parameters: Schema.Unknown as ToolSchema<unknown>,
|
||||
success: Schema.Unknown as ToolSchema<unknown>,
|
||||
execute: config.execute,
|
||||
toModelOutput: config.toModelOutput,
|
||||
_decode: Effect.succeed,
|
||||
_encode: Effect.succeed,
|
||||
_project: (parameters, callID, output) => project(config.toModelOutput, parameters, callID, output),
|
||||
_legacyResult: config.toModelOutput === undefined,
|
||||
_definition: new ToolDefinition({
|
||||
name: "",
|
||||
description: config.description,
|
||||
inputSchema: config.jsonSchema,
|
||||
outputSchema: config.outputSchema,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -143,18 +171,20 @@ export function make(config: TypedToolConfig | DynamicToolConfig): AnyTool {
|
|||
parameters: config.parameters,
|
||||
success: config.success,
|
||||
execute: config.execute,
|
||||
toModelOutput: config.toModelOutput,
|
||||
_decode: Schema.decodeUnknownEffect(config.parameters),
|
||||
_encode: Schema.encodeEffect(config.success),
|
||||
_project: (parameters, callID, output) => project(config.toModelOutput, parameters, callID, output),
|
||||
_legacyResult: false,
|
||||
_definition: new ToolDefinition({
|
||||
name: "",
|
||||
description: config.description,
|
||||
inputSchema: toJsonSchema(config.parameters),
|
||||
outputSchema: toJsonSchema(config.success),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
export const tool = make
|
||||
|
||||
/**
|
||||
* A record of named tools. The record key becomes the tool name on the wire.
|
||||
*/
|
||||
|
|
@ -162,8 +192,7 @@ export type Tools = Record<string, AnyTool>
|
|||
|
||||
/**
|
||||
* Convert a tools record into the `ToolDefinition[]` shape that
|
||||
* `LLMRequest.tools` expects. The runtime calls this internally; consumers
|
||||
* that build `LLMRequest` themselves can use it too.
|
||||
* `LLMRequest.tools` expects.
|
||||
*
|
||||
* Tool names come from the record keys, so the per-tool cached
|
||||
* `_definition` is rebuilt with the correct name here. The JSON Schema body
|
||||
|
|
@ -176,6 +205,7 @@ export const toDefinitions = (tools: Tools): ReadonlyArray<ToolDefinitionClass>
|
|||
name,
|
||||
description: item._definition.description,
|
||||
inputSchema: item._definition.inputSchema,
|
||||
outputSchema: item._definition.outputSchema,
|
||||
}),
|
||||
)
|
||||
|
||||
|
|
@ -185,6 +215,18 @@ const toJsonSchema = (schema: Schema.Top): JsonSchema.JsonSchema => {
|
|||
return { ...document.schema, $defs: document.definitions }
|
||||
}
|
||||
|
||||
const project = (
|
||||
toModelOutput: ((input: ToolModelOutputInput<any, any>) => ReadonlyArray<ToolContent>) | undefined,
|
||||
parameters: unknown,
|
||||
callID: ToolCallPart["id"],
|
||||
output: unknown,
|
||||
): ToolOutputType =>
|
||||
ToolOutput.make(
|
||||
output,
|
||||
toModelOutput?.({ callID, parameters, output }) ??
|
||||
(typeof output === "string" ? [toolText({ type: "text", text: output })] : []),
|
||||
)
|
||||
|
||||
export { ToolFailure }
|
||||
|
||||
export * as Tool from "./tool"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue