mirror of
https://github.com/Kaelio/ktx.git
synced 2026-07-01 08:59:39 +02:00
feat: add claude-code llm backend with runtime port (#115)
* docs: revise claude-code ingest backend spec * docs: keep claude-code spec focused on ingest * docs: expand claude-code spec to full llm parity * Refine claude-code backend spec after adversarial review iteration 1 * Refine claude-code backend spec after adversarial review iteration 2 * Refine claude-code backend spec after adversarial review iteration 3 * feat: recognize claude-code llm backend * feat: add ktx llm runtime port * feat: add claude-code llm runtime * feat: route non-agent llm calls through runtime * feat: run ingest agents through llm runtime * feat: support claude-code setup and status * test: verify claude-code backend runtime * docs: add claude-code backend v1 runtime plan * fix: close claude-code runtime isolation checks * fix: warn on claude-code prompt caching during setup * chore: verify claude-code v1 closure * docs: add claude-code backend v1 isolation closure plan * fix: update claude-code ingest setup guidance * docs: add claude-code backend v1 ingest guidance closure plan * docs: align claude-code isolation spec with sdk metadata * test: cover claude-code host discovery metadata * fix: tolerate claude-code host discovery metadata * docs: clarify claude-code host discovery metadata * docs: add claude-code auth-probe isolation fix plan * chore: prepare kaelio ktx rc1 release * chore: add semantic release workflow * fix: unblock ci checks * chore(release): 0.1.0-rc.1 * feat: add Claude Code model selection to setup * fix: keep git maintenance attached in local repos
This commit is contained in:
parent
e6d578c03f
commit
b565e44a22
109 changed files with 10218 additions and 1093 deletions
91
packages/context/src/llm/runtime-tools.ts
Normal file
91
packages/context/src/llm/runtime-tools.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { tool as aiTool, type Tool, type ToolSet } from 'ai';
|
||||
import { tool as claudeTool, type SdkMcpToolDefinition } from '@anthropic-ai/claude-agent-sdk';
|
||||
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
||||
import { z } from 'zod';
|
||||
import type { KtxRuntimeToolDescriptor, KtxRuntimeToolOutput, KtxRuntimeToolSet } from './runtime-port.js';
|
||||
|
||||
function isRuntimeOutput(value: unknown): value is KtxRuntimeToolOutput {
|
||||
return Boolean(
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
'markdown' in value &&
|
||||
typeof (value as { markdown?: unknown }).markdown === 'string',
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeKtxRuntimeToolOutput(value: unknown): KtxRuntimeToolOutput {
|
||||
if (isRuntimeOutput(value)) {
|
||||
return 'structured' in value ? { markdown: value.markdown, structured: value.structured } : { markdown: value.markdown };
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return { markdown: value };
|
||||
}
|
||||
return {
|
||||
markdown: `\`\`\`json\n${JSON.stringify(value, null, 2)}\n\`\`\``,
|
||||
structured: value,
|
||||
};
|
||||
}
|
||||
|
||||
function assertObjectSchema(name: string, schema: z.ZodType): asserts schema is z.ZodObject<z.ZodRawShape> {
|
||||
if (!(schema instanceof z.ZodObject)) {
|
||||
throw new Error(`KTX runtime tool "${name}" must use z.object input schema for claude-code`);
|
||||
}
|
||||
}
|
||||
|
||||
export function createAiSdkToolSet(tools: KtxRuntimeToolSet = {}): ToolSet {
|
||||
return Object.fromEntries(
|
||||
Object.entries(tools).map(([name, descriptor]) => [
|
||||
name,
|
||||
aiTool({
|
||||
description: descriptor.description,
|
||||
inputSchema: descriptor.inputSchema,
|
||||
execute: async (input) => descriptor.execute(input),
|
||||
toModelOutput: ({ output }) => {
|
||||
const normalized = normalizeKtxRuntimeToolOutput(output);
|
||||
return { type: 'text', value: normalized.markdown };
|
||||
},
|
||||
}),
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
export function createClaudeSdkTools(tools: KtxRuntimeToolSet = {}): Array<SdkMcpToolDefinition<z.ZodRawShape>> {
|
||||
return Object.values(tools).map((descriptor) => {
|
||||
assertObjectSchema(descriptor.name, descriptor.inputSchema);
|
||||
return claudeTool(
|
||||
descriptor.name,
|
||||
descriptor.description,
|
||||
descriptor.inputSchema.shape,
|
||||
async (input): Promise<CallToolResult> => {
|
||||
const normalized = normalizeKtxRuntimeToolOutput(await descriptor.execute(input));
|
||||
return { content: [{ type: 'text', text: normalized.markdown }] };
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function mcpToolIds(tools: KtxRuntimeToolSet = {}): string[] {
|
||||
return Object.keys(tools).map((name) => `mcp__ktx__${name}`);
|
||||
}
|
||||
|
||||
export function createRuntimeToolDescriptorFromAiTool(name: string, aiSdkTool: Tool): KtxRuntimeToolDescriptor {
|
||||
return {
|
||||
name,
|
||||
description: aiSdkTool.description ?? '',
|
||||
inputSchema: aiSdkTool.inputSchema as KtxRuntimeToolDescriptor['inputSchema'],
|
||||
execute: async (input) => {
|
||||
if (typeof aiSdkTool.execute !== 'function') {
|
||||
throw new Error(`KTX runtime tool "${name}" has no execute function`);
|
||||
}
|
||||
return normalizeKtxRuntimeToolOutput(
|
||||
await aiSdkTool.execute(input as never, { toolCallId: `runtime-${name}` } as never),
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createRuntimeToolSetFromAiSdkTools(tools: ToolSet = {}): KtxRuntimeToolSet {
|
||||
return Object.fromEntries(
|
||||
Object.entries(tools).map(([name, aiSdkTool]) => [name, createRuntimeToolDescriptorFromAiTool(name, aiSdkTool as Tool)]),
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue