wirejs-deploy-amplify-basic 0.1.149-llm → 0.1.151-llm

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.
@@ -3,6 +3,6 @@
3
3
  "dependencies": {
4
4
  "jsdom": "^25.0.1",
5
5
  "wirejs-dom": "^1.0.44",
6
- "wirejs-resources": "^0.1.149-llm"
6
+ "wirejs-resources": "^0.1.151-llm"
7
7
  }
8
8
  }
@@ -1,10 +1,11 @@
1
- import { LLM as BaseLLM, LLMMessage, ContinueConversationOptions, Resource } from 'wirejs-resources';
1
+ import { LLM as BaseLLM, AssistantMessage, ContinueConversationOptions, ToolDefinition, Resource } from 'wirejs-resources';
2
2
  export declare class LLM extends BaseLLM {
3
3
  private bedrockClient;
4
4
  constructor(scope: Resource | string, id: string, options: {
5
5
  models: string[];
6
6
  systemPrompt?: string;
7
7
  targetContextSize?: number;
8
+ tools?: ToolDefinition[];
8
9
  });
9
10
  private createBedrockInstructionMessage;
10
11
  private convertToBedrockFormat;
@@ -12,5 +13,5 @@ export declare class LLM extends BaseLLM {
12
13
  private invokeBedrock;
13
14
  private streamBedrock;
14
15
  private invokeModel;
15
- continueConversation({ history, onChunk, timeoutSeconds, systemPrompt, models, }: ContinueConversationOptions): Promise<LLMMessage>;
16
+ continueConversation({ history, onChunk, timeoutSeconds, systemPrompt, models, tools, }: ContinueConversationOptions): Promise<AssistantMessage>;
16
17
  }
@@ -18,12 +18,40 @@ export class LLM extends BaseLLM {
18
18
  };
19
19
  }
20
20
  convertToBedrockFormat(messages) {
21
- return messages.map(msg => ({
22
- role: msg.role,
23
- content: [{
24
- text: msg.content
25
- }]
26
- }));
21
+ return messages.map(msg => {
22
+ if (msg.role === 'tool') {
23
+ return {
24
+ role: 'user',
25
+ content: [{
26
+ toolResult: {
27
+ toolUseId: msg.tool_call_id,
28
+ content: [{ text: msg.content }]
29
+ }
30
+ }]
31
+ };
32
+ }
33
+ else if (msg.role === 'assistant' && 'tool_calls' in msg && msg.tool_calls) {
34
+ return {
35
+ role: 'assistant',
36
+ content: [
37
+ ...(msg.content ? [{ text: msg.content }] : []),
38
+ ...msg.tool_calls.map(tc => ({
39
+ toolUse: {
40
+ toolUseId: tc.id || `tool_${Date.now()}_${Math.random()}`,
41
+ name: tc.function.name,
42
+ input: tc.function.arguments
43
+ }
44
+ }))
45
+ ]
46
+ };
47
+ }
48
+ else {
49
+ return {
50
+ role: msg.role,
51
+ content: [{ text: msg.content }]
52
+ };
53
+ }
54
+ });
27
55
  }
28
56
  getModelId(model) {
29
57
  // small set of convenience identifier aliases. maybe worth breaking out into
@@ -39,14 +67,26 @@ export class LLM extends BaseLLM {
39
67
  };
40
68
  return modelMap[model] || model;
41
69
  }
42
- async invokeBedrock(modelId, systemPrompt, messages, stream, abortSignal) {
70
+ async invokeBedrock(modelId, systemPrompt, messages, stream, tools, abortSignal) {
71
+ const toolConfig = tools && tools.length > 0 ? {
72
+ tools: tools.map(tool => ({
73
+ toolSpec: {
74
+ name: tool.name,
75
+ description: tool.description,
76
+ inputSchema: {
77
+ json: tool.parameters
78
+ }
79
+ }
80
+ }))
81
+ } : undefined;
43
82
  if (stream) {
44
83
  const command = new ConverseStreamCommand({
45
84
  modelId,
46
85
  messages: this.convertToBedrockFormat(messages),
47
86
  system: systemPrompt ? [{
48
87
  text: systemPrompt
49
- }] : undefined
88
+ }] : undefined,
89
+ toolConfig
50
90
  });
51
91
  return await this.bedrockClient.send(command, { abortSignal });
52
92
  }
@@ -56,7 +96,8 @@ export class LLM extends BaseLLM {
56
96
  messages: this.convertToBedrockFormat(messages),
57
97
  system: systemPrompt ? [{
58
98
  text: systemPrompt
59
- }] : undefined
99
+ }] : undefined,
100
+ toolConfig
60
101
  });
61
102
  return await this.bedrockClient.send(command, { abortSignal });
62
103
  }
@@ -65,6 +106,8 @@ export class LLM extends BaseLLM {
65
106
  if (!response.stream)
66
107
  return null;
67
108
  let content = '';
109
+ let tool_calls = [];
110
+ let currentToolCall = null;
68
111
  for await (const chunk of response.stream) {
69
112
  // Check if request was aborted
70
113
  if (abortSignal?.aborted) {
@@ -90,29 +133,89 @@ export class LLM extends BaseLLM {
90
133
  }
91
134
  }
92
135
  }
136
+ // Handle tool use start
137
+ if (chunk.contentBlockStart?.start?.toolUse) {
138
+ const toolUse = chunk.contentBlockStart.start.toolUse;
139
+ currentToolCall = {
140
+ id: toolUse.toolUseId,
141
+ function: {
142
+ name: toolUse.name || '',
143
+ arguments: {}
144
+ }
145
+ };
146
+ }
147
+ // Handle tool use delta (input parameters)
148
+ if (chunk.contentBlockDelta?.delta?.toolUse?.input && currentToolCall) {
149
+ // The input from Bedrock is a string that needs to be parsed as JSON
150
+ const inputString = chunk.contentBlockDelta.delta.toolUse.input;
151
+ try {
152
+ const parsedInput = JSON.parse(inputString);
153
+ if (parsedInput && typeof parsedInput === 'object') {
154
+ currentToolCall.function.arguments = {
155
+ ...currentToolCall.function.arguments,
156
+ ...parsedInput
157
+ };
158
+ }
159
+ }
160
+ catch (error) {
161
+ console.warn('Failed to parse tool input JSON:', inputString, error);
162
+ }
163
+ }
164
+ // Handle tool use stop (finalize tool call)
165
+ if (chunk.contentBlockStop?.contentBlockIndex !== undefined && currentToolCall) {
166
+ tool_calls.push(currentToolCall);
167
+ currentToolCall = null;
168
+ }
93
169
  }
94
- return {
170
+ const result = {
95
171
  role: 'assistant',
96
172
  content
97
173
  };
174
+ if (tool_calls.length > 0) {
175
+ result.tool_calls = tool_calls;
176
+ }
177
+ return result;
98
178
  }
99
- async invokeModel(model, systemPrompt, history, onChunk, abortSignal, maxRetries = 10) {
179
+ async invokeModel(model, systemPrompt, history, onChunk, abortSignal, tools, maxRetries = 10) {
100
180
  const stream = typeof onChunk === 'function';
101
181
  for (let i = 0; i < maxRetries; i++) {
102
182
  try {
103
183
  const modelId = this.getModelId(model);
104
184
  if (modelId !== model)
105
185
  console.log(`Mapped ${model} to ${modelId}`);
106
- const response = await this.invokeBedrock(modelId, systemPrompt, history, stream, abortSignal);
186
+ const response = await this.invokeBedrock(modelId, systemPrompt, history, stream, tools, abortSignal);
107
187
  if (stream) {
108
188
  return this.streamBedrock(response, onChunk, abortSignal);
109
189
  }
110
190
  else {
111
- const data = JSON.parse(new TextDecoder().decode(response.body));
112
- return {
191
+ // Handle non-streaming response with potential tool calls
192
+ const content = response.output?.message?.content;
193
+ let resultContent = '';
194
+ let tool_calls = [];
195
+ if (content) {
196
+ for (const block of content) {
197
+ if (block.text) {
198
+ resultContent += block.text;
199
+ }
200
+ else if (block.toolUse) {
201
+ tool_calls.push({
202
+ id: block.toolUse.toolUseId,
203
+ function: {
204
+ name: block.toolUse.name,
205
+ arguments: block.toolUse.input || {}
206
+ }
207
+ });
208
+ }
209
+ }
210
+ }
211
+ const result = {
113
212
  role: 'assistant',
114
- content: data.content?.[0]?.text || 'No response generated'
213
+ content: resultContent || 'No response generated'
115
214
  };
215
+ if (tool_calls.length > 0) {
216
+ result.tool_calls = tool_calls;
217
+ }
218
+ return result;
116
219
  }
117
220
  }
118
221
  catch (error) {
@@ -131,7 +234,7 @@ export class LLM extends BaseLLM {
131
234
  }
132
235
  throw new Error("Exceeded max retries trying to invoke " + model);
133
236
  }
134
- async continueConversation({ history, onChunk, timeoutSeconds, systemPrompt, models, }) {
237
+ async continueConversation({ history, onChunk, timeoutSeconds, systemPrompt, models, tools, }) {
135
238
  const controller = new AbortController();
136
239
  let timeoutId;
137
240
  if (timeoutSeconds) {
@@ -144,7 +247,7 @@ export class LLM extends BaseLLM {
144
247
  // throw a fit wins.
145
248
  for (const model of models ?? this.models) {
146
249
  try {
147
- const result = await this.invokeModel(model, (systemPrompt ?? this.systemPrompt) ?? '', history, onChunk, controller.signal);
250
+ const result = await this.invokeModel(model, (systemPrompt ?? this.systemPrompt) ?? '', history, onChunk, controller.signal, tools ?? this.tools);
148
251
  if (!result)
149
252
  throw new Error("No response from model.");
150
253
  if (timeoutId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wirejs-deploy-amplify-basic",
3
- "version": "0.1.149-llm",
3
+ "version": "0.1.151-llm",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -44,7 +44,7 @@
44
44
  "recursive-copy": "^2.0.14",
45
45
  "rimraf": "^6.0.1",
46
46
  "wirejs-dom": "^1.0.44",
47
- "wirejs-resources": "^0.1.149-llm"
47
+ "wirejs-resources": "^0.1.151-llm"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@aws-amplify/backend": "^1.14.0",