agent-pulse 1.4.4 → 1.5.0

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.
@@ -72,12 +72,17 @@ class GoogleProvider {
72
72
  }
73
73
  if (m.tool_calls) {
74
74
  for (const tc of m.tool_calls) {
75
- parts.push({
75
+ const fcPart = {
76
76
  functionCall: {
77
77
  name: tc.name,
78
78
  args: tc.arguments
79
79
  }
80
- });
80
+ };
81
+ // Preserve thought_signature for Gemini 3+ models
82
+ if (tc.thought_signature) {
83
+ fcPart.thoughtSignature = tc.thought_signature;
84
+ }
85
+ parts.push(fcPart);
81
86
  }
82
87
  }
83
88
  if (m.content) {
@@ -225,20 +230,18 @@ class GoogleProvider {
225
230
  // Tool Calls
226
231
  // New SDK usually aggregates or provides them in chunks.
227
232
  // We need to check `candidates[0].content.parts` for `functionCall`.
228
- // Streaming tool calls might be tricky as they come in parts?
229
- // Google usually sends the full function call in one chunk or at the end?
233
+ // Streaming tool calls might be tricky as they come in parts?
234
+ // Google usually sends the full function call in one chunk or at the end?
230
235
  // Let's inspect the chunk structure via candidates.
231
236
  const parts = chunk.candidates?.[0]?.content?.parts;
232
237
  if (parts) {
233
238
  for (const part of parts) {
234
239
  if (part.functionCall) {
235
- // It seems Google sends full function call or we need to accumulate?
236
- // Usually it's complete in the response object if stream is done, but in stream?
237
- // Let's collect them.
238
240
  toolCalls.push({
239
241
  name: part.functionCall.name,
240
- arguments: part.functionCall.args, // Already parsed JSON usually
241
- id: 'call_' + Date.now() // Google doesn't always send ID, need to check
242
+ arguments: part.functionCall.args,
243
+ id: 'call_' + Date.now(),
244
+ thought_signature: part.thoughtSignature || undefined
242
245
  });
243
246
  }
244
247
  }
@@ -23,17 +23,51 @@ class GrokProvider {
23
23
  const promptText = Array.isArray(prompt)
24
24
  ? prompt.filter(m => m.role === 'user').map(m => m.content).join('\n')
25
25
  : String(prompt);
26
- // Both generation and editing use /v1/images/generations
27
- // For editing, just add image_url to the same request
28
26
  const imageUrl = config?.reference_image || config?.image_url;
29
- const response = await this.client.images.generate({
30
- model: this.model,
31
- prompt: promptText,
32
- n: config?.n || 1,
33
- response_format: config?.response_format || 'b64_json',
34
- ...(config?.aspect_ratio && { aspect_ratio: config.aspect_ratio }),
35
- ...(imageUrl && { image_url: imageUrl }),
36
- });
27
+ let response;
28
+ if (imageUrl) {
29
+ // Image EDITING: use /v1/images/edits with JSON body (not multipart/form-data)
30
+ // The OpenAI SDK's images.edit() uses multipart/form-data which xAI doesn't support,
31
+ // so we make a direct fetch call with application/json
32
+ const editBody = {
33
+ model: this.model,
34
+ prompt: promptText,
35
+ n: config?.n || 1,
36
+ response_format: config?.response_format || 'b64_json',
37
+ image: {
38
+ url: imageUrl,
39
+ type: 'image_url',
40
+ },
41
+ };
42
+ // For multi-image editing, aspect_ratio can override input ratio
43
+ if (config?.aspect_ratio) {
44
+ editBody.aspect_ratio = config.aspect_ratio;
45
+ }
46
+ const res = await fetch('https://api.x.ai/v1/images/edits', {
47
+ method: 'POST',
48
+ headers: {
49
+ 'Content-Type': 'application/json',
50
+ 'Authorization': `Bearer ${this.client.apiKey}`,
51
+ },
52
+ body: JSON.stringify(editBody),
53
+ });
54
+ if (!res.ok) {
55
+ const errorText = await res.text();
56
+ throw new Error(`xAI image edit failed (${res.status}): ${errorText}`);
57
+ }
58
+ response = await res.json();
59
+ }
60
+ else {
61
+ // Image GENERATION: text-to-image via /v1/images/generations
62
+ const genResponse = await this.client.images.generate({
63
+ model: this.model,
64
+ prompt: promptText,
65
+ n: config?.n || 1,
66
+ response_format: config?.response_format || 'b64_json',
67
+ ...(config?.aspect_ratio && { aspect_ratio: config.aspect_ratio }),
68
+ });
69
+ response = genResponse;
70
+ }
37
71
  const images = response.data;
38
72
  const markdownParts = images.map((img, i) => {
39
73
  if (img.b64_json) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-pulse",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "A lightweight, agentic AI framework for JavaScript/TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",