@stacksjs/ai 0.75.71 → 0.75.73
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/openai/index.js +3 -3
- package/dist/text.js +1 -1
- package/dist/types.d.ts +8 -2
- 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/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 block=data.content.find((b)=>b.type==="tool_use")??data.content.find((b)=>b.type==="text")??data.content[0],result={content:block?.type==="tool_use"?JSON.stringify(block.input):block?.text??"",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:"auto",disable_parallel_tool_use:!0};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==="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 resultContent(output){const blocks=output.output.message?.content??[],toolUse=blocks.find((block)=>("toolUse"in block));if(toolUse)return JSON.stringify(toolUse.toolUse.input);return blocks.filter((block)=>("text"in block)).map((block)=>block.text).join("")}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),result={content:resultContent(response),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,3 +1,3 @@
|
|
|
1
|
-
import{fetchWithRetry}from"../../utils/retry";import{recordUsage}from"../../utils/usage";import{normalizeMessagesForProvider}from"../../utils/vision";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
|
|
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{recordUsage}from"../../utils/usage";import{normalizeMessagesForProvider}from"../../utils/vision";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=normalizeMessagesForProvider(messages,"openai"),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 result={content:data.choices[0].message.content,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:normalizeMessagesForProvider(messages,"openai")})});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/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
|
@@ -183,6 +183,7 @@ export declare interface ConfiguredAIOptions extends AIConfig {
|
|
|
183
183
|
anthropic?: AIDriverConfig & { anthropicVersion?: string }
|
|
184
184
|
openai?: AIDriverConfig & { embeddingModel?: string }
|
|
185
185
|
ollama?: AIDriverConfig & { host?: string, embeddingModel?: string }
|
|
186
|
+
bedrock?: { model?: string, region?: string, maxTokens?: number }
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
export declare interface GenerateObjectOptions extends ChatCompletionOptions {
|
|
@@ -214,5 +215,10 @@ export type AIResponseFormat = | { type: 'text' }
|
|
|
214
215
|
strict?: boolean
|
|
215
216
|
}
|
|
216
217
|
}
|
|
217
|
-
export type AIProvider = 'anthropic' | 'openai' | 'ollama';
|
|
218
|
-
|
|
218
|
+
export type AIProvider = 'anthropic' | 'openai' | 'ollama' | 'bedrock';
|
|
219
|
+
/**
|
|
220
|
+
* Where a provider's credentials come from. `aws-credential-chain`: Bedrock
|
|
221
|
+
* resolves them per request from the environment, a shared profile, or the
|
|
222
|
+
* instance role, so whether they exist is only known when one is made.
|
|
223
|
+
*/
|
|
224
|
+
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}
|
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.73",
|
|
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.73",
|
|
69
|
+
"@stacksjs/config": "0.75.73",
|
|
70
|
+
"@stacksjs/storage": "0.75.73",
|
|
71
|
+
"@stacksjs/ts-cloud": "^0.16.39"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@anthropic-ai/claude-agent-sdk": "^0.2.79"
|