@stacksjs/ai 0.75.72 → 0.75.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +9 -0
- package/dist/client.js +3 -3
- package/dist/drivers/anthropic/index.js +2 -2
- package/dist/drivers/bedrock/index.d.ts +29 -0
- package/dist/drivers/bedrock/index.js +2 -0
- package/dist/drivers/index.d.ts +2 -0
- package/dist/drivers/index.js +1 -1
- package/dist/drivers/ollama/index.js +2 -2
- package/dist/drivers/openai/index.js +3 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/text.js +1 -1
- package/dist/types.d.ts +28 -3
- package/dist/utils/client-bedrock-runtime.d.ts +15 -2
- package/dist/utils/client-bedrock-runtime.js +1 -1
- package/dist/utils/client-bedrock.js +1 -1
- package/dist/utils/model-access.d.ts +21 -1
- package/dist/utils/model-access.js +1 -1
- package/dist/utils/tools.d.ts +28 -0
- package/dist/utils/tools.js +1 -0
- package/dist/utils/vision.js +1 -1
- package/package.json +5 -5
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { AIProvider, AIProviderConfiguration, ConfiguredAIClient, ConfiguredAIOptions } from './types';
|
|
2
2
|
export declare function resolveAIProvider(config: ConfiguredAIOptions, override?: AIProvider): AIProvider;
|
|
3
|
+
/**
|
|
4
|
+
* A Bedrock model or inference profile id: `<provider>.<model>`, optionally
|
|
5
|
+
* behind a geography (`us.`, `global.`), or a Bedrock ARN.
|
|
6
|
+
*
|
|
7
|
+
* `anthropic.claude-...` used to select the first-party Anthropic driver,
|
|
8
|
+
* which sent the Bedrock id to api.anthropic.com as a model name it does not
|
|
9
|
+
* have. A first-party Claude model is `claude-...`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isBedrockModelId(id: string): boolean;
|
|
3
12
|
/**
|
|
4
13
|
* Inspect the selected provider without creating a request or exposing secrets.
|
|
5
14
|
* Applications can use this for health checks, settings screens, and fallbacks.
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as anthropic from"./drivers/anthropic";import*as ollama from"./drivers/ollama";import*as openai from"./drivers/openai";function textContent(content){if(typeof content==="string")return content;return content.filter((block)=>block.type==="text").map((block)=>block.text??"").join(`
|
|
2
|
-
`)}export function resolveAIProvider(config,override){if(override)return override;const configured=String(config.default||"").toLowerCase();if(configured==="anthropic"||configured.startsWith("
|
|
1
|
+
import*as anthropic from"./drivers/anthropic";import*as bedrock from"./drivers/bedrock";import*as ollama from"./drivers/ollama";import*as openai from"./drivers/openai";function textContent(content){if(typeof content==="string")return content;return content.filter((block)=>block.type==="text").map((block)=>block.text??"").join(`
|
|
2
|
+
`)}export function resolveAIProvider(config,override){if(override)return override;const configured=String(config.default||"").toLowerCase();if(configured==="bedrock"||isBedrockModelId(configured))return"bedrock";if(configured==="anthropic"||configured.startsWith("claude-"))return"anthropic";if(configured==="openai"||configured.startsWith("gpt-")||configured.startsWith("o1")||configured.startsWith("o3"))return"openai";if(configured==="ollama")return"ollama";throw Error(`Unsupported configured AI driver: ${config.default||"(empty)"}. Expected anthropic, openai, ollama, bedrock, or a model id.`)}export function isBedrockModelId(id){return/^arn:aws[\w-]*:bedrock:/.test(id)||/^(?:(?:global|us|us-gov|eu|apac|jp|au|ca)\.)?(?:amazon|anthropic|meta|mistral|cohere|ai21|deepseek|openai|qwen|writer|stability)\.[a-z0-9]/.test(id)}function configuredModel(config,provider){const driverModel=config.drivers?.[provider]?.model?.trim();if(driverModel)return driverModel;const configuredDefault=String(config.default||"").trim();if(!configuredDefault||configuredDefault.toLowerCase()===provider)return;return configuredDefault}export function getAIProviderConfiguration(config,override,environment=process.env){const provider=resolveAIProvider(config,override),model=configuredModel(config,provider);if(provider==="ollama")return{provider,model,configured:!0,source:"local"};if(provider==="bedrock")return{provider,model:model??(environment.BEDROCK_MODEL_ID||bedrock.DEFAULT_BEDROCK_MODEL),configured:!0,source:"aws-credential-chain"};const driver=config.drivers?.[provider]??{};if(driver.apiKey?.trim())return{provider,model,configured:!0,source:"config"};if(environment[provider==="openai"?"OPENAI_API_KEY":"ANTHROPIC_API_KEY"]?.trim())return{provider,model,configured:!0,source:"environment"};if(driver.baseUrl?.trim())return{provider,model,configured:!0,source:"base-url"};return{provider,model,configured:!1,source:"none"}}function parseObject(content){const unfenced=content.trim().replace(/^```(?:json)?\s*/i,"").replace(/\s*```$/,"");try{return JSON.parse(unfenced)}catch{const start=unfenced.indexOf("{"),end=unfenced.lastIndexOf("}");if(start>=0&&end>start)return JSON.parse(unfenced.slice(start,end+1));throw Error("AI response did not contain a valid JSON object")}}function typeMatches(value,type){if(type==="array")return Array.isArray(value);if(type==="integer")return typeof value==="number"&&Number.isInteger(value);if(type==="object")return typeof value==="object"&&value!==null&&!Array.isArray(value);if(type==="null")return value===null;return typeof value===type}function validateSchema(value,schema,path="$"){const errors=[];if(schema.type){const allowed=Array.isArray(schema.type)?schema.type:[schema.type];if(!allowed.some((type)=>typeMatches(value,type)))return[`${path} must be ${allowed.join(" or ")}`]}if(schema.enum&&!schema.enum.some((candidate)=>Object.is(candidate,value)))errors.push(`${path} must be one of ${schema.enum.join(", ")}`);if(typeof value==="string"){if(schema.minLength!==void 0&&value.length<schema.minLength)errors.push(`${path} must contain at least ${schema.minLength} characters`);if(schema.maxLength!==void 0&&value.length>schema.maxLength)errors.push(`${path} must contain at most ${schema.maxLength} characters`)}if(typeof value==="number"){if(schema.minimum!==void 0&&value<schema.minimum)errors.push(`${path} must be at least ${schema.minimum}`);if(schema.maximum!==void 0&&value>schema.maximum)errors.push(`${path} must be at most ${schema.maximum}`)}if(Array.isArray(value)){if(schema.minItems!==void 0&&value.length<schema.minItems)errors.push(`${path} must contain at least ${schema.minItems} items`);if(schema.maxItems!==void 0&&value.length>schema.maxItems)errors.push(`${path} must contain at most ${schema.maxItems} items`);if(schema.items)value.forEach((item,index)=>errors.push(...validateSchema(item,schema.items,`${path}[${index}]`)))}if(typeof value==="object"&&value!==null&&!Array.isArray(value)){const record=value;for(const key of schema.required??[])if(!(key in record))errors.push(`${path}.${key} is required`);for(const[key,child]of Object.entries(schema.properties??{}))if(key in record)errors.push(...validateSchema(record[key],child,`${path}.${key}`));if(schema.additionalProperties===!1){const known=new Set(Object.keys(schema.properties??{}));for(const key of Object.keys(record))if(!known.has(key))errors.push(`${path}.${key} is not allowed`)}}return errors}function createGenerate(config,provider){if(provider==="anthropic"){const driver=config.drivers?.anthropic??{};anthropic.configure({apiKey:driver.apiKey||process.env.ANTHROPIC_API_KEY||"",model:driver.model,maxTokens:driver.maxTokens,anthropicVersion:driver.anthropicVersion});return async(messages,options={})=>{const systemMessages=messages.filter((message)=>message.role==="system").map((message)=>textContent(message.content)),nonSystem=messages.filter((message)=>message.role!=="system");return anthropic.chat(nonSystem,{...options,system:[options.system,...systemMessages].filter(Boolean).join(`
|
|
3
3
|
|
|
4
|
-
`)||void 0})}}if(provider==="openai"){const driver=config.drivers?.openai??{};openai.configure({apiKey:driver.apiKey||process.env.OPENAI_API_KEY||"",model:driver.model,maxTokens:driver.maxTokens,baseUrl:driver.baseUrl,embeddingModel:driver.embeddingModel});return(messages,options={})=>{const finalMessages=options.system?[{role:"system",content:options.system},...messages]:messages;return openai.chat(finalMessages,options)}}const driver=config.drivers?.ollama??{};ollama.configure({host:driver.host||driver.baseUrl,model:driver.model,embeddingModel:driver.embeddingModel});return(messages,options={})=>{const finalMessages=options.system?[{role:"system",content:options.system},...messages]:messages;return ollama.chat(finalMessages,options)}}export function createAIClient(config,override){const provider=resolveAIProvider(config,override),configuration=getAIProviderConfiguration(config,provider),generate=createGenerate(config,provider);return{provider,configuration,generate,async generateObject(messages,schema,options={}){const{attempts=2,system,...completionOptions}=options,history=[...messages];let lastError;for(let attempt=1;attempt<=Math.max(1,attempts);attempt++){const result=await generate(history,{...completionOptions,system,responseFormat:{type:"json_schema",json_schema:{name:"structured_output",schema,strict:!0}}});try{const data=parseObject(result.content),errors=validateSchema(data,schema);if(errors.length)throw Error(`AI response failed schema validation: ${errors.slice(0,8).join("; ")}`);return{data,result}}catch(error){lastError=error instanceof Error?error:Error(String(error));if(attempt<attempts){history.push({role:"assistant",content:result.content});history.push({role:"user",content:`Return only a corrected JSON object. Validation error: ${lastError.message}`})}}}throw lastError??Error("AI object generation failed")}}}export{parseObject as parseAIObject,validateSchema as validateAISchema};
|
|
4
|
+
`)||void 0})}}if(provider==="bedrock"){const driver=config.drivers?.bedrock??{};bedrock.configure({model:configuredModel(config,"bedrock"),region:driver.region,maxTokens:driver.maxTokens});return(messages,options={})=>bedrock.chat(messages,options)}if(provider==="openai"){const driver=config.drivers?.openai??{};openai.configure({apiKey:driver.apiKey||process.env.OPENAI_API_KEY||"",model:driver.model,maxTokens:driver.maxTokens,baseUrl:driver.baseUrl,embeddingModel:driver.embeddingModel});return(messages,options={})=>{const finalMessages=options.system?[{role:"system",content:options.system},...messages]:messages;return openai.chat(finalMessages,options)}}const driver=config.drivers?.ollama??{};ollama.configure({host:driver.host||driver.baseUrl,model:driver.model,embeddingModel:driver.embeddingModel});return(messages,options={})=>{const finalMessages=options.system?[{role:"system",content:options.system},...messages]:messages;return ollama.chat(finalMessages,options)}}export function createAIClient(config,override){const provider=resolveAIProvider(config,override),configuration=getAIProviderConfiguration(config,provider),generate=createGenerate(config,provider);return{provider,configuration,generate,async generateObject(messages,schema,options={}){const{attempts=2,system,...completionOptions}=options,history=[...messages];let lastError;for(let attempt=1;attempt<=Math.max(1,attempts);attempt++){const result=await generate(history,{...completionOptions,system,responseFormat:{type:"json_schema",json_schema:{name:"structured_output",schema,strict:!0}}});try{const data=parseObject(result.content),errors=validateSchema(data,schema);if(errors.length)throw Error(`AI response failed schema validation: ${errors.slice(0,8).join("; ")}`);return{data,result}}catch(error){lastError=error instanceof Error?error:Error(String(error));if(attempt<attempts){history.push({role:"assistant",content:result.content});history.push({role:"user",content:`Return only a corrected JSON object. Validation error: ${lastError.message}`})}}}throw lastError??Error("AI object generation failed")}}}export{parseObject as parseAIObject,validateSchema as validateAISchema};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{fetchWithRetry}from"../../utils/retry";import{recordUsage}from"../../utils/usage";import{normalizeMessagesForProvider}from"../../utils/vision";import{DEFAULT_ANTHROPIC_MODEL}from"../../models";const DEFAULT_MODEL=DEFAULT_ANTHROPIC_MODEL,DEFAULT_MAX_TOKENS=4096,DEFAULT_VERSION="2023-06-01",BASE_URL="https://api.anthropic.com/v1";let globalConfig=null;export function configure(config){globalConfig=config}function getConfig(config){const merged={...globalConfig,...config};if(!merged.apiKey)merged.apiKey=process.env.ANTHROPIC_API_KEY||"";return merged}export function createAnthropicDriver(config){const{apiKey,model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,anthropicVersion=DEFAULT_VERSION}=config;return{name:"Claude API",async process(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":apiKey,"anthropic-version":anthropicVersion},body:JSON.stringify({model,max_tokens:maxTokens,system:systemPrompt,messages:[...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");return data.content[0].text},async*stream(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system,tools,toolChoice,responseFormat}=options,normalizedMessages=normalizeMessagesForProvider(messages,"anthropic"),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,messages:normalizedMessages};if(tools&&tools.length>0){body.tools=tools.map((t)=>({name:t.name,description:t.description,input_schema:t.parameters??{type:"object",properties:{}}}));if(toolChoice!==void 0)body.tool_choice=mapAnthropicToolChoice(toolChoice)}if(responseFormat&&responseFormat.type!=="text"){const outputTool=buildAnthropicJsonTool(responseFormat),existing=Array.isArray(body.tools)?body.tools:[];body.tools=[...existing,outputTool];if(toolChoice===void 0)body.tool_choice={type:"tool",name:outputTool.name}}const response=await
|
|
1
|
+
import{fetchWithRetry}from"../../utils/retry";import{recordUsage}from"../../utils/usage";import{normalizeMessagesForProvider}from"../../utils/vision";import{DEFAULT_ANTHROPIC_MODEL}from"../../models";const DEFAULT_MODEL=DEFAULT_ANTHROPIC_MODEL,DEFAULT_MAX_TOKENS=4096,DEFAULT_VERSION="2023-06-01",BASE_URL="https://api.anthropic.com/v1";let globalConfig=null;export function configure(config){globalConfig=config}function getConfig(config){const merged={...globalConfig,...config};if(!merged.apiKey)merged.apiKey=process.env.ANTHROPIC_API_KEY||"";return merged}export function createAnthropicDriver(config){const{apiKey,model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,anthropicVersion=DEFAULT_VERSION}=config;return{name:"Claude API",async process(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":apiKey,"anthropic-version":anthropicVersion},body:JSON.stringify({model,max_tokens:maxTokens,system:systemPrompt,messages:[...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");return data.content[0].text},async*stream(command,systemPrompt,history){if(!apiKey)throw Error("Anthropic API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":apiKey,"anthropic-version":anthropicVersion},body:JSON.stringify({model,max_tokens:maxTokens,system:systemPrompt,stream:!0,messages:[...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";const handlePayload=function*(data){if(data==="[DONE]")return;let event;try{event=JSON.parse(data)}catch{return}if(event.type==="error"){const msg=event.error?.message??JSON.stringify(event.error??event);throw Error(`[anthropic/stream] mid-stream error: ${msg}`)}if(event.type==="content_block_delta"&&event.delta?.text)yield event.delta.text};while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system,tools,toolChoice,responseFormat}=options,normalizedMessages=normalizeMessagesForProvider(messages,"anthropic"),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,messages:normalizedMessages};if(tools&&tools.length>0){body.tools=tools.map((t)=>({name:t.name,description:t.description,input_schema:t.parameters??{type:"object",properties:{}}}));if(toolChoice!==void 0)body.tool_choice=mapAnthropicToolChoice(toolChoice)}if(responseFormat&&responseFormat.type!=="text"){const outputTool=buildAnthropicJsonTool(responseFormat),existing=Array.isArray(body.tools)?body.tools:[];body.tools=[...existing,outputTool];if(toolChoice===void 0)body.tool_choice={type:"tool",name:outputTool.name}}const response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":config.apiKey,"anthropic-version":config.anthropicVersion||DEFAULT_VERSION},body:JSON.stringify(body)});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const data=await response.json();if(!data.content||data.content.length===0)throw Error("Claude API returned empty content");const outputTool=responseFormat&&responseFormat.type!=="text"?buildAnthropicJsonTool(responseFormat).name:void 0,blocks=data.content,structured=outputTool?blocks.find((b)=>b.type==="tool_use"&&b.name===outputTool):void 0,toolCalls=blocks.filter((b)=>b.type==="tool_use"&&b!==structured).map((b)=>({id:b.id??"",name:b.name??"",arguments:b.input??{}})),result={content:structured?JSON.stringify(structured.input):blocks.filter((b)=>b.type==="text").map((b)=>b.text??"").join(""),...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.usage?.input_tokens||0,completionTokens:data.usage?.output_tokens||0,totalTokens:(data.usage?.input_tokens||0)+(data.usage?.output_tokens||0)},finishReason:data.stop_reason};recordUsage({provider:"anthropic",model:data.model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}function mapAnthropicToolChoice(choice){if(choice==="auto")return{type:"auto"};if(choice==="required")return{type:"any"};if(choice==="none")return{type:"none"};return{type:"tool",name:choice.name}}function buildAnthropicJsonTool(format){if(format.type==="json_schema")return{name:format.json_schema.name,description:`Returns the result as JSON matching the '${format.json_schema.name}' schema.`,input_schema:format.json_schema.schema};return{name:"structured_output",description:"Returns the result as a JSON object.",input_schema:{type:"object",additionalProperties:!0}}}export async function*streamChat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,system}=options,response=await fetchWithRetry(`${BASE_URL}/messages`,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":config.apiKey,"anthropic-version":config.anthropicVersion||DEFAULT_VERSION},body:JSON.stringify({model,max_tokens:maxTokens,temperature,top_p:topP,stop_sequences:stop?Array.isArray(stop)?stop:[stop]:void 0,system,stream:!0,messages:normalizeMessagesForProvider(messages,"anthropic")})});if(!response.ok){const error=await response.text();throw Error(`Claude API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
3
|
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: ")){const data=line.slice(6);if(data==="[DONE]")continue;try{const event=JSON.parse(data);if(event.type==="content_block_delta"&&event.delta?.text)yield event.delta.text}catch{}}}}export async function prompt(text,options={}){return(await chat([{role:"user",content:text}],options)).content}export function estimateTokens(text){return Math.ceil(text.length/4)}export const anthropicDriver={create:createAnthropicDriver},anthropic={configure,chat,streamChat,prompt,estimateTokens,createDriver:createAnthropicDriver};export default anthropic;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AIMessage, AIResult, ChatCompletionOptions } from '../../types';
|
|
2
|
+
import type { ConverseCommandInput } from '@stacksjs/ts-cloud/aws';
|
|
3
|
+
export declare function configure(config: BedrockDriverConfig): void;
|
|
4
|
+
/**
|
|
5
|
+
* Split AI messages into Converse's `system` and `messages`. System messages
|
|
6
|
+
* become system blocks, which Converse keeps apart from the turns.
|
|
7
|
+
*/
|
|
8
|
+
export declare function toConverseMessages(messages: AIMessage[], system?: string): Pick<ConverseCommandInput, 'messages' | 'system'>;
|
|
9
|
+
/**
|
|
10
|
+
* One chat completion. `options.model` overrides the configured model.
|
|
11
|
+
*/
|
|
12
|
+
export declare function chat(messages: AIMessage[], options?: ChatCompletionOptions & { system?: string }): Promise<AIResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Invocable on demand, with no inference profile and no agreement, so it
|
|
15
|
+
* answers in a fresh account. The Titan Text model this replaces as the
|
|
16
|
+
* default for `ask()` and `summarize()` has been retired.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_BEDROCK_MODEL: 'amazon.nova-lite-v1:0';
|
|
19
|
+
export declare const bedrock: {
|
|
20
|
+
configure: typeof configure;
|
|
21
|
+
chat: typeof chat
|
|
22
|
+
};
|
|
23
|
+
export declare interface BedrockDriverConfig {
|
|
24
|
+
model?: string
|
|
25
|
+
region?: string
|
|
26
|
+
maxTokens?: number
|
|
27
|
+
}
|
|
28
|
+
declare type ConverseMessage = ConverseCommandInput['messages'][number];
|
|
29
|
+
declare type ConverseBlock = ConverseMessage['content'][number];
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{converse}from"../../utils/client-bedrock-runtime";import{recordUsage}from"../../utils/usage";export const DEFAULT_BEDROCK_MODEL="amazon.nova-lite-v1:0";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function resolveConfig(){return{...globalConfig,model:globalConfig.model||process.env.BEDROCK_MODEL_ID||DEFAULT_BEDROCK_MODEL}}const IMAGE_FORMATS=new Set(["png","jpeg","gif","webp"]);function imageBlock(mediaType,data){const format=mediaType.replace(/^image\//,"").replace("jpg","jpeg");if(!IMAGE_FORMATS.has(format))throw Error(`Bedrock Converse takes png, jpeg, gif or webp images, not ${mediaType}`);return{image:{format,source:{bytes:data}}}}function contentBlock(part){if(part.type==="text")return{text:part.text??""};if(part.type==="tool_call"&&part.toolCall)return{toolUse:{toolUseId:part.toolCall.id,name:part.toolCall.name,input:part.toolCall.arguments}};if(part.type==="tool_result"&&part.toolResult)return{toolResult:{toolUseId:part.toolResult.toolCallId,content:[{text:part.toolResult.content}],...part.toolResult.isError?{status:"error"}:{}}};if(part.type==="image"&&part.source)return imageBlock(part.source.media_type,part.source.data);if(part.type==="image_url"&&part.image_url){const inline=/^data:([^;]+);base64,(.*)$/.exec(part.image_url.url);if(!inline)throw Error("Bedrock Converse takes image bytes, not a URL: pass the image as a data: URL or a base64 `image` block");return imageBlock(inline[1],inline[2])}throw Error(`Unsupported message content for Bedrock: ${part.type}`)}export function toConverseMessages(messages,system){const systemBlocks=system?[{text:system}]:[],turns=[];for(const message of messages){if(message.role==="system"){const text=typeof message.content==="string"?message.content:message.content.filter((part)=>part.type==="text").map((part)=>part.text??"").join(`
|
|
2
|
+
`);if(text)systemBlocks.push({text});continue}turns.push({role:message.role,content:typeof message.content==="string"?[{text:message.content}]:message.content.map(contentBlock)})}return{messages:turns,...systemBlocks.length>0?{system:systemBlocks}:{}}}function structuredOutputTool(format){if(format.type==="json_schema")return{name:format.json_schema.name,description:`Returns the result as JSON matching the '${format.json_schema.name}' schema.`,inputSchema:{json:format.json_schema.schema}};return{name:"structured_output",description:"Returns the result as a JSON object.",inputSchema:{json:{type:"object",additionalProperties:!0}}}}function toolConfig(options){const{tools=[],toolChoice,responseFormat}=options,specs=toolChoice==="none"?[]:tools.map((tool)=>({name:tool.name,description:tool.description,inputSchema:{json:tool.parameters??{type:"object",properties:{}}}}));let choice;if(toolChoice==="required")choice={any:{}};else if(toolChoice&&typeof toolChoice==="object")choice={tool:{name:toolChoice.name}};else if(toolChoice==="auto")choice={auto:{}};if(responseFormat&&responseFormat.type!=="text"){const output=structuredOutputTool(responseFormat);specs.push(output);choice=choice??{tool:{name:output.name}}}if(specs.length===0)return;return{tools:specs.map((toolSpec)=>({toolSpec})),...choice?{toolChoice:choice}:{}}}function readOutput(output,outputTool){const blocks=output.output.message?.content??[],uses=blocks.filter((block)=>("toolUse"in block)),structured=outputTool?uses.find((block)=>block.toolUse.name===outputTool):void 0,toolCalls=uses.filter((block)=>block!==structured).map((block)=>({id:block.toolUse.toolUseId,name:block.toolUse.name,arguments:block.toolUse.input??{}}));return{content:structured?JSON.stringify(structured.toolUse.input):blocks.filter((block)=>("text"in block)).map((block)=>block.text).join(""),...toolCalls.length>0?{toolCalls}:{}}}export async function chat(messages,options={}){const config=resolveConfig(),model=options.model||config.model,startedAt=Date.now(),inferenceConfig={},maxTokens=options.maxTokens??config.maxTokens;if(maxTokens!==void 0)inferenceConfig.maxTokens=maxTokens;if(options.temperature!==void 0)inferenceConfig.temperature=options.temperature;if(options.topP!==void 0)inferenceConfig.topP=options.topP;if(options.stop!==void 0)inferenceConfig.stopSequences=Array.isArray(options.stop)?options.stop:[options.stop];const tools=toolConfig(options),response=await converse({modelId:model,...toConverseMessages(messages,options.system),...Object.keys(inferenceConfig).length>0?{inferenceConfig}:{},...tools?{toolConfig:tools}:{}},config.region),outputTool=options.responseFormat&&options.responseFormat.type!=="text"?structuredOutputTool(options.responseFormat).name:void 0,result={...readOutput(response,outputTool),model,usage:{promptTokens:response.usage?.inputTokens??0,completionTokens:response.usage?.outputTokens??0,totalTokens:response.usage?.totalTokens??0},finishReason:response.stopReason};recordUsage({provider:"bedrock",model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}export const bedrock={configure,chat};
|
package/dist/drivers/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type { AnthropicDriverConfig } from './anthropic/index';
|
|
2
2
|
export type { OpenAIDriverConfig } from './openai/index';
|
|
3
|
+
export type { BedrockDriverConfig } from './bedrock/index';
|
|
3
4
|
export type { OllamaDriverConfig } from './ollama/index';
|
|
4
5
|
export type { ClaudeAgentSDKConfig } from './claude-agent-sdk/index';
|
|
5
6
|
/**
|
|
@@ -10,4 +11,5 @@ export type { ClaudeAgentSDKConfig } from './claude-agent-sdk/index';
|
|
|
10
11
|
export { createAnthropicDriver, anthropicDriver, anthropic, estimateTokens } from './anthropic/index';
|
|
11
12
|
export { createOpenAIDriver, openaiDriver, openai } from './openai/index';
|
|
12
13
|
export { createOllamaDriver, ollamaDriver, ollama } from './ollama/index';
|
|
14
|
+
export { bedrock, DEFAULT_BEDROCK_MODEL, toConverseMessages } from './bedrock/index';
|
|
13
15
|
export { createClaudeAgentSDKDriver, claudeAgentSDK, getLastSessionId, clearSession } from './claude-agent-sdk/index';
|
package/dist/drivers/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{createAnthropicDriver,anthropicDriver,anthropic,estimateTokens}from"./anthropic";export{createOpenAIDriver,openaiDriver,openai}from"./openai";export{createOllamaDriver,ollamaDriver,ollama}from"./ollama";export{createClaudeAgentSDKDriver,claudeAgentSDK,getLastSessionId,clearSession}from"./claude-agent-sdk";
|
|
1
|
+
export{createAnthropicDriver,anthropicDriver,anthropic,estimateTokens}from"./anthropic";export{createOpenAIDriver,openaiDriver,openai}from"./openai";export{createOllamaDriver,ollamaDriver,ollama}from"./ollama";export{bedrock,DEFAULT_BEDROCK_MODEL,toConverseMessages}from"./bedrock";export{createClaudeAgentSDKDriver,claudeAgentSDK,getLastSessionId,clearSession}from"./claude-agent-sdk";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const DEFAULT_HOST="http://localhost:11434",DEFAULT_MODEL="llama3.2",DEFAULT_EMBEDDING_MODEL="nomic-embed-text";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function getConfig(config){return{host:config?.host||globalConfig.host||process.env.OLLAMA_HOST||DEFAULT_HOST,model:config?.model||globalConfig.model||process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel:config?.embeddingModel||globalConfig.embeddingModel||DEFAULT_EMBEDDING_MODEL}}export function createOllamaDriver(config={}){const{host=process.env.OLLAMA_HOST||DEFAULT_HOST,model=process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"Ollama",async process(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).message.content},async*stream(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}},async embed(input){const inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}}}export async function chat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop,responseFormat}=options,format=responseFormat?.type==="json_schema"?responseFormat.json_schema.schema:responseFormat?.type==="json_object"?"json":void 0,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages,stream:!1,format,options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json();return{content:data.message.
|
|
1
|
+
import{parseToolArguments,toOllamaMessages}from"../../utils/tools";const DEFAULT_HOST="http://localhost:11434",DEFAULT_MODEL="llama3.2",DEFAULT_EMBEDDING_MODEL="nomic-embed-text";let globalConfig={};export function configure(config){globalConfig={...globalConfig,...config}}function getConfig(config){return{host:config?.host||globalConfig.host||process.env.OLLAMA_HOST||DEFAULT_HOST,model:config?.model||globalConfig.model||process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel:config?.embeddingModel||globalConfig.embeddingModel||DEFAULT_EMBEDDING_MODEL}}export function createOllamaDriver(config={}){const{host=process.env.OLLAMA_HOST||DEFAULT_HOST,model=process.env.OLLAMA_MODEL||DEFAULT_MODEL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"Ollama",async process(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).message.content},async*stream(command,systemPrompt,history){const response=await fetch(`${host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}],stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}},async embed(input){const inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}}}export async function chat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop,responseFormat,tools,toolChoice}=options;if(toolChoice==="required"||toolChoice&&typeof toolChoice==="object")throw Error("Ollama cannot be made to call a tool: offer the tools with toolChoice 'auto', or use another driver");const offered=toolChoice==="none"?[]:tools??[],format=responseFormat?.type==="json_schema"?responseFormat.json_schema.schema:responseFormat?.type==="json_object"?"json":void 0,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:toOllamaMessages(messages),stream:!1,format,...offered.length>0?{tools:offered.map((tool)=>({type:"function",function:{name:tool.name,description:tool.description,parameters:tool.parameters??{type:"object",properties:{}}}}))}:{},options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json(),toolCalls=(data.message?.tool_calls??[]).map((call,index)=>({id:call.id??`call_${index}`,name:call.function.name,arguments:parseToolArguments(call.function.arguments,call.function.name)}));return{content:data.message?.content??"",...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.prompt_eval_count||0,completionTokens:data.eval_count||0,totalTokens:(data.prompt_eval_count||0)+(data.eval_count||0)},finishReason:data.done_reason||"stop"}}export async function*streamChat(messages,options={}){const config=getConfig(),{model=config.model,temperature,topP,stop}=options,response=await fetch(`${config.host}/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model,messages:toOllamaMessages(messages),stream:!0,options:{temperature,top_p:topP,stop}})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
3
|
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(data.message?.content)yield data.message.content}catch{}}}}export async function generate(prompt,options={}){const config=getConfig(),response=await fetch(`${config.host}/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:options.model||config.model,prompt,system:options.system,template:options.template,context:options.context,raw:options.raw,format:options.format,images:options.images,stream:!1})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const data=await response.json();return{content:data.response,model:data.model,usage:{promptTokens:data.prompt_eval_count||0,completionTokens:data.eval_count||0,totalTokens:(data.prompt_eval_count||0)+(data.eval_count||0)}}}export async function embed(input,model){const config=getConfig(),embeddingModel=model||config.embeddingModel,inputs=Array.isArray(input)?input:[input],embeddings=[];for(const text of inputs){const response=await fetch(`${config.host}/api/embeddings`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:embeddingModel,prompt:text})});if(!response.ok){const error=await response.text();throw Error(`Ollama Embeddings API error: ${error}`)}const data=await response.json();embeddings.push(data.embedding)}return Array.isArray(input)?embeddings:embeddings[0]}export async function listModels(){const config=getConfig(),response=await fetch(`${config.host}/api/tags`,{method:"GET"});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return(await response.json()).models}export async function pullModel(name,onProgress){const config=getConfig(),response=await fetch(`${config.host}/api/pull`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name,stream:!0})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}const reader=response.body?.getReader();if(!reader)return;const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
4
4
|
`);buffer=lines.pop()||"";for(const line of lines){if(!line.trim())continue;try{const data=JSON.parse(line);if(onProgress)onProgress(data.status??"",data.completed,data.total)}catch{}}}}export async function deleteModel(name){const config=getConfig(),response=await fetch(`${config.host}/api/delete`,{method:"DELETE",headers:{"Content-Type":"application/json"},body:JSON.stringify({name})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}}export async function showModel(_name){const config=getConfig(),response=await fetch(`${config.host}/api/show`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:_name})});if(!response.ok){const error=await response.text();throw Error(`Ollama API error: ${error}`)}return response.json()}export async function isRunning(){const config=getConfig();try{return(await fetch(`${config.host}/api/tags`,{method:"GET"})).ok}catch{return!1}}export const ollamaDriver={create:createOllamaDriver},ollama={configure,chat,streamChat,generate,embed,listModels,pullModel,deleteModel,showModel,isRunning,createDriver:createOllamaDriver};export default ollama;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{fetchWithRetry}from"../../utils/retry";import{
|
|
2
|
-
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))},async embed(input){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await
|
|
3
|
-
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: ")){const data=line.slice(6);if(data==="[DONE]")continue;try{const content=JSON.parse(data).choices[0]?.delta?.content;if(content)yield content}catch{}}}}export async function embed(input,model=DEFAULT_EMBEDDING_MODEL){const config=getConfig(),response=await
|
|
1
|
+
import{fetchWithRetry}from"../../utils/retry";import{parseToolArguments,toChatCompletionMessages}from"../../utils/tools";import{recordUsage}from"../../utils/usage";const DEFAULT_MODEL="gpt-4o",DEFAULT_MAX_TOKENS=4096,DEFAULT_EMBEDDING_MODEL="text-embedding-3-small",DEFAULT_BASE_URL="https://api.openai.com/v1";let globalConfig=null;export function configure(config){globalConfig=config}function getConfig(config){const merged={...globalConfig,...config};if(!merged.apiKey)merged.apiKey=process.env.OPENAI_API_KEY||"";return merged}export function createOpenAIDriver(config){const{apiKey,model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,baseUrl=DEFAULT_BASE_URL,embeddingModel=DEFAULT_EMBEDDING_MODEL}=config;return{name:"OpenAI",async process(command,systemPrompt,history){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const data=await response.json();if(!data.choices||data.choices.length===0)throw Error("OpenAI API returned empty choices");return data.choices[0].message.content},async*stream(command,systemPrompt,history){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,stream:!0,messages:[{role:"system",content:systemPrompt},...history,{role:"user",content:command}]})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";const handlePayload=function*(data){if(data==="[DONE]")return;let parsed;try{parsed=JSON.parse(data)}catch{return}if(parsed?.error){const msg=parsed.error.message||JSON.stringify(parsed.error);throw Error(`[openai/stream] mid-stream error: ${msg}`)}const content=parsed.choices?.[0]?.delta?.content;if(content)yield content};while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
2
|
+
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: "))yield*handlePayload(line.slice(6))}if(buffer.startsWith("data: "))yield*handlePayload(buffer.slice(6))},async embed(input){if(!apiKey)throw Error("OpenAI API key not set. Configure your API key in settings.");const response=await fetchWithRetry(`${baseUrl}/embeddings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${apiKey}`},body:JSON.stringify({model:embeddingModel,input})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Embeddings API error: ${error}`)}const data=await response.json();if(Array.isArray(input))return data.data.map((d)=>d.embedding);return data.data[0].embedding}}}export async function chat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop,tools,toolChoice,responseFormat}=options,normalizedMessages=toChatCompletionMessages(messages,{argumentsAsString:!0}),startedAt=Date.now(),body={model,max_tokens:maxTokens,temperature,top_p:topP,stop,messages:normalizedMessages};if(tools?.length)body.tools=tools.map((tool)=>({type:"function",function:{name:tool.name,description:tool.description,parameters:tool.parameters??{type:"object",properties:{}}}}));if(toolChoice!==void 0)body.tool_choice=typeof toolChoice==="object"?{type:"function",function:{name:toolChoice.name}}:toolChoice;if(responseFormat&&responseFormat.type!=="text")body.response_format=responseFormat;const response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify(body)});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const data=await response.json();if(!data.choices||data.choices.length===0)throw Error("OpenAI API returned empty choices");const message=data.choices[0].message,toolCalls=(message.tool_calls??[]).map((call)=>({id:call.id,name:call.function.name,arguments:parseToolArguments(call.function.arguments,call.function.name)})),result={content:message.content??"",...toolCalls.length>0?{toolCalls}:{},model:data.model,usage:{promptTokens:data.usage?.prompt_tokens||0,completionTokens:data.usage?.completion_tokens||0,totalTokens:data.usage?.total_tokens||0},finishReason:data.choices[0].finish_reason};recordUsage({provider:"openai",model:data.model,promptTokens:result.usage.promptTokens,completionTokens:result.usage.completionTokens,totalTokens:result.usage.totalTokens,durationMs:Date.now()-startedAt,timestamp:Date.now()});return result}export async function*streamChat(messages,options={}){const config=getConfig(),{model=DEFAULT_MODEL,maxTokens=DEFAULT_MAX_TOKENS,temperature,topP,stop}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/chat/completions`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,max_tokens:maxTokens,temperature,top_p:topP,stop,stream:!0,messages:toChatCompletionMessages(messages,{argumentsAsString:!0})})});if(!response.ok){const error=await response.text();throw Error(`OpenAI API error: ${error}`)}const reader=response.body?.getReader();if(!reader)throw Error("No response body");const decoder=new TextDecoder;let buffer="";while(!0){const{done,value}=await reader.read();if(done)break;buffer+=decoder.decode(value,{stream:!0});const lines=buffer.split(`
|
|
3
|
+
`);buffer=lines.pop()||"";for(const line of lines)if(line.startsWith("data: ")){const data=line.slice(6);if(data==="[DONE]")continue;try{const content=JSON.parse(data).choices[0]?.delta?.content;if(content)yield content}catch{}}}}export async function embed(input,model=DEFAULT_EMBEDDING_MODEL){const config=getConfig(),response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/embeddings`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,input})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Embeddings API error: ${error}`)}const data=await response.json();if(Array.isArray(input))return data.data.map((d)=>d.embedding);return data.data[0].embedding}export async function generateImage(prompt,options={}){const config=getConfig(),{model="dall-e-3",size="1024x1024",quality="standard",n=1,responseFormat="url"}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/images/generations`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,prompt,size,quality,n,response_format:responseFormat})});if(!response.ok){const error=await response.text();throw Error(`OpenAI Image API error: ${error}`)}return(await response.json()).data}export async function transcribe(audioFile,options={}){const config=getConfig(),formData=new FormData;formData.append("file",audioFile);formData.append("model",options.model||"whisper-1");if(options.language)formData.append("language",options.language);if(options.prompt)formData.append("prompt",options.prompt);if(options.responseFormat)formData.append("response_format",options.responseFormat);if(options.temperature!==void 0)formData.append("temperature",String(options.temperature));const response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/audio/transcriptions`,{method:"POST",headers:{Authorization:`Bearer ${config.apiKey}`},body:formData});if(!response.ok){const error=await response.text();throw Error(`OpenAI Whisper API error: ${error}`)}return response.json()}export async function textToSpeech(input,options={}){const config=getConfig(),{model="tts-1",voice="alloy",responseFormat="mp3",speed=1}=options,response=await fetchWithRetry(`${config.baseUrl||DEFAULT_BASE_URL}/audio/speech`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${config.apiKey}`},body:JSON.stringify({model,input,voice,response_format:responseFormat,speed})});if(!response.ok){const error=await response.text();throw Error(`OpenAI TTS API error: ${error}`)}return response.arrayBuffer()}export const openaiDriver={create:createOpenAIDriver},openai={configure,chat,streamChat,embed,generateImage,transcribe,textToSpeech,createDriver:createOpenAIDriver};export default openai;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,9 @@ export * from './utils/client-bedrock-runtime';
|
|
|
35
35
|
// translates between the OpenAI image_url and Anthropic image
|
|
36
36
|
// source formats so apps can switch providers without rewriting.
|
|
37
37
|
export { buildMessageWithImages, normalizeMessagesForProvider } from './utils/vision';
|
|
38
|
+
// Tool round trips across drivers: `AIResult.toolCalls` in, then the
|
|
39
|
+
// assistant's turn and the results back out.
|
|
40
|
+
export { assistantTurn, toolResultsTurn } from './utils/tools';
|
|
38
41
|
// HTTP retry helper for 429/5xx (stacksjs/stacks#1878 A-5).
|
|
39
42
|
export { fetchWithRetry } from './utils/retry';
|
|
40
43
|
// Usage tracking (stacksjs/stacks#1878 A-6). Apps install reporters
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./types";export*from"./drivers";export*from"./client";export*from"./agents";export*from"./buddy";export*from"./context";export*from"./text";export*from"./image";export*from"./speech";export*from"./search";export*from"./personalization";export*from"./mcp";export*from"./models";export*from"./utils/client-bedrock";export*from"./utils/client-bedrock-runtime";export{buildMessageWithImages,normalizeMessagesForProvider}from"./utils/vision";export{fetchWithRetry}from"./utils/retry";export{clearUsageReporters,listUsageReporters,onUsage,recordUsage}from"./utils/usage";export{estimateMessageTokens,estimateTokens,sanitizePrompt}from"./utils/tokens";export*from"./utils/model-access";
|
|
1
|
+
export*from"./types";export*from"./drivers";export*from"./client";export*from"./agents";export*from"./buddy";export*from"./context";export*from"./text";export*from"./image";export*from"./speech";export*from"./search";export*from"./personalization";export*from"./mcp";export*from"./models";export*from"./utils/client-bedrock";export*from"./utils/client-bedrock-runtime";export{buildMessageWithImages,normalizeMessagesForProvider}from"./utils/vision";export{assistantTurn,toolResultsTurn}from"./utils/tools";export{fetchWithRetry}from"./utils/retry";export{clearUsageReporters,listUsageReporters,onUsage,recordUsage}from"./utils/usage";export{estimateMessageTokens,estimateTokens,sanitizePrompt}from"./utils/tokens";export*from"./utils/model-access";
|
package/dist/text.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ai}from"@stacksjs/config";import{chat,configure}from"./drivers/bedrock";async function complete(prompt,options,failure){const{maxTokenCount=512,temperature=0,topP=0.9,modelId}=options,driver=ai.drivers?.bedrock;configure({model:driver?.model,region:driver?.region});let result;try{result=await chat([{role:"user",content:prompt}],{model:modelId,maxTokens:maxTokenCount,temperature,topP})}catch(error){throw Error(`${failure}: ${error.message}`)}return result.content}export function summarize(text,options={}){return complete(`Summarize the following text: ${text}`,options,"Error summarizing text")}export function ask(question,options={}){return complete(question,options,"Error asking question")}
|
package/dist/types.d.ts
CHANGED
|
@@ -8,10 +8,28 @@ export declare interface AIMessage {
|
|
|
8
8
|
content: string | AIMessageContent[]
|
|
9
9
|
}
|
|
10
10
|
export declare interface AIMessageContent {
|
|
11
|
-
type: 'text' | 'image_url' | 'image'
|
|
11
|
+
type: 'text' | 'image_url' | 'image' | 'tool_call' | 'tool_result'
|
|
12
12
|
text?: string
|
|
13
13
|
image_url?: { url: string, detail?: 'auto' | 'low' | 'high' }
|
|
14
14
|
source?: { type: 'base64', media_type: string, data: string }
|
|
15
|
+
toolCall?: AIToolCall
|
|
16
|
+
toolResult?: AIToolResult
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A tool the model asked to call. The same shape from every driver: OpenAI's
|
|
20
|
+
* `tool_calls`, Anthropic's `tool_use`, Bedrock's `toolUse`, Ollama's
|
|
21
|
+
* `tool_calls`.
|
|
22
|
+
*/
|
|
23
|
+
export declare interface AIToolCall {
|
|
24
|
+
id: string
|
|
25
|
+
name: string
|
|
26
|
+
arguments: Record<string, unknown>
|
|
27
|
+
}
|
|
28
|
+
export declare interface AIToolResult {
|
|
29
|
+
toolCallId: string
|
|
30
|
+
name?: string
|
|
31
|
+
content: string
|
|
32
|
+
isError?: boolean
|
|
15
33
|
}
|
|
16
34
|
export declare interface AIDriver {
|
|
17
35
|
name: string
|
|
@@ -65,6 +83,7 @@ export declare interface ChatCompletionOptions {
|
|
|
65
83
|
}
|
|
66
84
|
export declare interface AIResult {
|
|
67
85
|
content: string
|
|
86
|
+
toolCalls?: AIToolCall[]
|
|
68
87
|
model: string
|
|
69
88
|
usage?: {
|
|
70
89
|
promptTokens: number
|
|
@@ -183,6 +202,7 @@ export declare interface ConfiguredAIOptions extends AIConfig {
|
|
|
183
202
|
anthropic?: AIDriverConfig & { anthropicVersion?: string }
|
|
184
203
|
openai?: AIDriverConfig & { embeddingModel?: string }
|
|
185
204
|
ollama?: AIDriverConfig & { host?: string, embeddingModel?: string }
|
|
205
|
+
bedrock?: { model?: string, region?: string, maxTokens?: number }
|
|
186
206
|
}
|
|
187
207
|
}
|
|
188
208
|
export declare interface GenerateObjectOptions extends ChatCompletionOptions {
|
|
@@ -214,5 +234,10 @@ export type AIResponseFormat = | { type: 'text' }
|
|
|
214
234
|
strict?: boolean
|
|
215
235
|
}
|
|
216
236
|
}
|
|
217
|
-
export type AIProvider = 'anthropic' | 'openai' | 'ollama';
|
|
218
|
-
|
|
237
|
+
export type AIProvider = 'anthropic' | 'openai' | 'ollama' | 'bedrock';
|
|
238
|
+
/**
|
|
239
|
+
* Where a provider's credentials come from. `aws-credential-chain`: Bedrock
|
|
240
|
+
* resolves them per request from the environment, a shared profile, or the
|
|
241
|
+
* instance role, so whether they exist is only known when one is made.
|
|
242
|
+
*/
|
|
243
|
+
export type AIConfigurationSource = 'config' | 'environment' | 'base-url' | 'local' | 'aws-credential-chain' | 'none';
|
|
@@ -1,5 +1,18 @@
|
|
|
1
|
-
import type { InvokeModelCommandInput, InvokeModelCommandOutput, InvokeModelWithResponseStreamCommandInput, InvokeModelWithResponseStreamCommandOutput } from '@stacksjs/ts-cloud/aws';
|
|
2
|
-
export type { InvokeModelCommandInput, InvokeModelWithResponseStreamCommandInput };
|
|
1
|
+
import type { ConverseCommandInput, ConverseCommandOutput, InvokeModelCommandInput, InvokeModelCommandOutput, InvokeModelWithResponseStreamCommandInput, InvokeModelWithResponseStreamCommandOutput } from '@stacksjs/ts-cloud/aws';
|
|
2
|
+
export type { ConverseCommandInput, ConverseCommandOutput, InvokeModelCommandInput, InvokeModelWithResponseStreamCommandInput };
|
|
3
|
+
/**
|
|
4
|
+
* The region Bedrock is called in: the one passed, then `AWS_REGION`, then
|
|
5
|
+
* `AWS_DEFAULT_REGION` (what the scaffolded `.env` sets), then us-east-1.
|
|
6
|
+
*
|
|
7
|
+
* These clients used to read `REGION`, which nothing sets, so Bedrock was
|
|
8
|
+
* always called in us-east-1 whatever the application configured.
|
|
9
|
+
*/
|
|
10
|
+
export declare function bedrockRegion(region?: string): string;
|
|
11
|
+
/*
|
|
12
|
+
* Converse - one request shape for every Bedrock text model
|
|
13
|
+
* @see https://docs.aws.amazon.com/bedrock/latest/APIReference/API_runtime_Converse.html
|
|
14
|
+
*/
|
|
15
|
+
export declare function converse(params: ConverseCommandInput, region?: string): Promise<ConverseCommandOutput>;
|
|
3
16
|
/*
|
|
4
17
|
* Invoke Model
|
|
5
18
|
* @param {InvokeModelCommandInput} params
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import process from"node:process";
|
|
1
|
+
import process from"node:process";export function bedrockRegion(region){return region||process.env.AWS_REGION||process.env.AWS_DEFAULT_REGION||"us-east-1"}const clients=new Map;async function getClient(region){const resolved=bedrockRegion(region),cached=clients.get(resolved);if(cached)return cached;const mod=await import("@stacksjs/ts-cloud/aws");if(!mod?.BedrockRuntimeClient)throw Error("@stacksjs/ts-cloud/aws does not export BedrockRuntimeClient - rebuild ts-cloud or remove the AI dependency.");const client=new mod.BedrockRuntimeClient(resolved);clients.set(resolved,client);return client}export async function converse(params,region){return(await getClient(region)).converse(params)}export async function invokeModel(params){return(await getClient()).invokeModel(params)}export async function invokeModelWithResponseStream(params){return(await getClient()).invokeModelWithResponseStream(params)}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import{bedrockRegion}from"./client-bedrock-runtime";let _client=null;async function getClient(){if(_client)return _client;const mod=await import("@stacksjs/ts-cloud/aws");if(!mod?.BedrockClient)throw Error("@stacksjs/ts-cloud/aws does not export BedrockClient - rebuild ts-cloud or remove the AI dependency.");_client=new mod.BedrockClient(bedrockRegion());return _client}export async function createModelCustomizationJob(param){return(await getClient()).createModelCustomizationJob(param)}export async function getModelCustomizationJob(params){return(await getClient()).getModelCustomizationJob(params)}export async function listFoundationModels(params){return(await getClient()).listFoundationModels(params)}
|
|
@@ -1 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The foundation model an invocation id names: `us.anthropic.claude-sonnet-5-5`
|
|
3
|
+
* and `global.anthropic.claude-sonnet-5-5` are inference profiles for
|
|
4
|
+
* `anthropic.claude-sonnet-5-5`. Access is granted per foundation model, and
|
|
5
|
+
* Bedrock's availability check refuses a `us.` profile id as invalid.
|
|
6
|
+
*/
|
|
7
|
+
export declare function foundationModelId(modelId: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Make each model invocable in this account: a no-op for one that already
|
|
10
|
+
* is, an agreement from its public offer for a marketplace model that is not.
|
|
11
|
+
*
|
|
12
|
+
* Defaults to the models in `config/ai.ts`. Each model is reported on its own,
|
|
13
|
+
* so one refused model does not hide the rest. This used to call a Bedrock
|
|
14
|
+
* endpoint that does not exist and log the error for every model.
|
|
15
|
+
*/
|
|
16
|
+
export declare function requestModelAccess(models?: readonly string[], options?: { region?: string }): Promise<ModelAccessResult[]>;
|
|
17
|
+
export declare interface ModelAccessResult {
|
|
18
|
+
modelId: string
|
|
19
|
+
status: 'available' | 'requested' | 'failed'
|
|
20
|
+
error?: string
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{ai}from"@stacksjs/config";import{bedrockRegion}from"./client-bedrock-runtime";async function getBedrockClient(region){const mod=await import("@stacksjs/ts-cloud/aws");if(!mod?.BedrockClient)throw Error("@stacksjs/ts-cloud/aws does not export BedrockClient - rebuild ts-cloud or remove the AI dependency.");return new mod.BedrockClient(region)}export function foundationModelId(modelId){return modelId.replace(/^(?:global|us|us-gov|eu|apac|jp|au|ca)\./,"")}export async function requestModelAccess(models=ai.models??[],options={}){if(models.length===0)throw Error("No AI models to request access to. List them under `models` in config/ai.ts.");const client=await getBedrockClient(bedrockRegion(options.region)),results=[];for(const modelId of models)try{const{status}=await client.requestModelAccess({modelId:foundationModelId(modelId)});results.push({modelId,status})}catch(error){results.push({modelId,status:"failed",error:error.message})}return results}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AIMessage, AIResult, AIToolResult } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The assistant message that records `result`, to send back with the results.
|
|
4
|
+
*/
|
|
5
|
+
export declare function assistantTurn(result: Pick<AIResult, 'content' | 'toolCalls'>): AIMessage;
|
|
6
|
+
/**
|
|
7
|
+
* The user message that answers the calls of the previous assistant turn.
|
|
8
|
+
*/
|
|
9
|
+
export declare function toolResultsTurn(results: AIToolResult[]): AIMessage;
|
|
10
|
+
/**
|
|
11
|
+
* Parse a tool call's arguments. OpenAI sends them as a JSON string, the
|
|
12
|
+
* others as an object; a string that is not a JSON object is a model error,
|
|
13
|
+
* reported rather than swallowed.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseToolArguments(raw: unknown, tool: string): Record<string, unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Messages in the OpenAI chat-completions shape, which Ollama shares: an
|
|
18
|
+
* assistant's calls ride on the message as `tool_calls`, and each result is a
|
|
19
|
+
* message of its own with role `tool`. Everything else is normalised as before.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toChatCompletionMessages(messages: AIMessage[], options: { argumentsAsString: boolean }): Array<Record<string, unknown>>;
|
|
22
|
+
/**
|
|
23
|
+
* Messages in Ollama's `/api/chat` shape: `content` is a string, images ride
|
|
24
|
+
* in `images` as bare base64, a call's arguments are an object, and a result
|
|
25
|
+
* names its tool. Content arrays used to be sent as they were, which Ollama
|
|
26
|
+
* rejects.
|
|
27
|
+
*/
|
|
28
|
+
export declare function toOllamaMessages(messages: AIMessage[]): Array<Record<string, unknown>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{normalizeMessagesForProvider}from"./vision";export function assistantTurn(result){if(!result.toolCalls?.length)return{role:"assistant",content:result.content};const content=result.content?[{type:"text",text:result.content}]:[];for(const call of result.toolCalls)content.push({type:"tool_call",toolCall:call});return{role:"assistant",content}}export function toolResultsTurn(results){return{role:"user",content:results.map((toolResult)=>({type:"tool_result",toolResult}))}}export function parseToolArguments(raw,tool){if(raw===void 0||raw===null||raw==="")return{};if(typeof raw!=="string")return raw;try{const parsed=JSON.parse(raw);if(parsed&&typeof parsed==="object"&&!Array.isArray(parsed))return parsed}catch{}throw Error(`The model called "${tool}" with arguments that are not a JSON object: ${raw.slice(0,200)}`)}function blocksOf(message){return typeof message.content==="string"?[{type:"text",text:message.content}]:message.content}function textOf(blocks){return blocks.filter((block)=>block.type==="text").map((block)=>block.text??"").join("")}export function toChatCompletionMessages(messages,options){const out=[];for(const message of messages){const blocks=blocksOf(message),calls=blocks.filter((block)=>block.type==="tool_call"&&block.toolCall).map((block)=>block.toolCall),results=blocks.filter((block)=>block.type==="tool_result"&&block.toolResult).map((block)=>block.toolResult);if(calls.length===0&&results.length===0){out.push(normalizeMessagesForProvider([message],"openai")[0]);continue}if(calls.length>0)out.push({role:"assistant",content:textOf(blocks)||null,tool_calls:calls.map((call)=>({id:call.id,type:"function",function:{name:call.name,arguments:options.argumentsAsString?JSON.stringify(call.arguments):call.arguments}}))});for(const result of results)out.push({role:"tool",tool_call_id:result.toolCallId,...result.name?{tool_name:result.name}:{},content:result.content});const remaining=blocks.filter((block)=>block.type!=="tool_call"&&block.type!=="tool_result");if(calls.length===0&&remaining.length>0)out.push(normalizeMessagesForProvider([{role:message.role,content:remaining}],"openai")[0])}return out}export function toOllamaMessages(messages){return toChatCompletionMessages(messages,{argumentsAsString:!1}).map((message)=>{if(message.role==="tool")return{role:"tool",content:message.content,...message.tool_name?{tool_name:message.tool_name}:{}};const out={role:message.role};if(Array.isArray(message.content)){const parts=message.content;out.content=parts.filter((part)=>part.type==="text").map((part)=>part.text??"").join("");const images=parts.filter((part)=>part.type==="image_url").map((part)=>{const inline=/^data:[^;]+;base64,(.*)$/.exec(part.image_url?.url??"");if(!inline)throw Error("Ollama takes image bytes, not a URL: pass the image as a data: URL or a base64 `image` block");return inline[1]});if(images.length>0)out.images=images}else out.content=message.content??"";if(message.tool_calls)out.tool_calls=message.tool_calls.map((call)=>({function:call.function}));return out})}
|
package/dist/utils/vision.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function toOpenAIContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image_url")return{type:"image_url",image_url:block.image_url};if(block.type==="image"&&block.source){if(block.source.type==="base64")return{type:"image_url",image_url:{url:`data:${block.source.media_type};base64,${block.source.data}`}}}return block}function toAnthropicContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image"&&block.source)return{type:"image",source:block.source};if(block.type==="image_url"&&block.image_url){const url=block.image_url.url,dataMatch=url.match(/^data:([^;]+);base64,(.+)$/);if(dataMatch)return{type:"image",source:{type:"base64",media_type:dataMatch[1],data:dataMatch[2]}};return{type:"image",source:{type:"url",url}}}return block}export function normalizeMessagesForProvider(messages,provider){return messages.map((msg)=>{if(typeof msg.content==="string")return msg;const mapper=provider==="openai"?toOpenAIContent:toAnthropicContent;return{role:msg.role,content:msg.content.map(mapper)}})}export function buildMessageWithImages(command,images){const blocks=[];for(const img of images)if(img.dataBase64&&img.mediaType)blocks.push({type:"image",source:{type:"base64",media_type:img.mediaType,data:img.dataBase64}});else if(img.url)blocks.push({type:"image_url",image_url:{url:img.url,detail:img.detail}});blocks.push({type:"text",text:command});return blocks}
|
|
1
|
+
function toOpenAIContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="image_url")return{type:"image_url",image_url:block.image_url};if(block.type==="image"&&block.source){if(block.source.type==="base64")return{type:"image_url",image_url:{url:`data:${block.source.media_type};base64,${block.source.data}`}}}return block}function toAnthropicContent(block){if(block.type==="text")return{type:"text",text:block.text??""};if(block.type==="tool_call"&&block.toolCall)return{type:"tool_use",id:block.toolCall.id,name:block.toolCall.name,input:block.toolCall.arguments};if(block.type==="tool_result"&&block.toolResult)return{type:"tool_result",tool_use_id:block.toolResult.toolCallId,content:block.toolResult.content,...block.toolResult.isError?{is_error:!0}:{}};if(block.type==="image"&&block.source)return{type:"image",source:block.source};if(block.type==="image_url"&&block.image_url){const url=block.image_url.url,dataMatch=url.match(/^data:([^;]+);base64,(.+)$/);if(dataMatch)return{type:"image",source:{type:"base64",media_type:dataMatch[1],data:dataMatch[2]}};return{type:"image",source:{type:"url",url}}}return block}export function normalizeMessagesForProvider(messages,provider){return messages.map((msg)=>{if(typeof msg.content==="string")return msg;const mapper=provider==="openai"?toOpenAIContent:toAnthropicContent;return{role:msg.role,content:msg.content.map(mapper)}})}export function buildMessageWithImages(command,images){const blocks=[];for(const img of images)if(img.dataBase64&&img.mediaType)blocks.push({type:"image",source:{type:"base64",media_type:img.mediaType,data:img.dataBase64}});else if(img.url)blocks.push({type:"image_url",image_url:{url:img.url,detail:img.detail}});blocks.push({type:"text",text:command});return blocks}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/ai",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.74",
|
|
6
6
|
"description": "Stacks Artificial Intelligence.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -65,10 +65,10 @@
|
|
|
65
65
|
"prepublishOnly": "bun run build"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@stacksjs/cli": "0.75.
|
|
69
|
-
"@stacksjs/config": "0.75.
|
|
70
|
-
"@stacksjs/storage": "0.75.
|
|
71
|
-
"@stacksjs/ts-cloud": "^0.16.
|
|
68
|
+
"@stacksjs/cli": "0.75.74",
|
|
69
|
+
"@stacksjs/config": "0.75.74",
|
|
70
|
+
"@stacksjs/storage": "0.75.74",
|
|
71
|
+
"@stacksjs/ts-cloud": "^0.16.39"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@anthropic-ai/claude-agent-sdk": "^0.2.79"
|