clarity-ai 4.3.0 → 4.3.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/chat.js +61 -32
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clarity-ai",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
4
4
  "description": "Premium terminal AI agent for Termux — OpenCode-style UI, streaming, markdown, tools, agent mode",
5
5
  "type": "module",
6
6
  "bin": {
package/src/chat.js CHANGED
@@ -3,6 +3,8 @@ import { setKey } from './config/keys.js';
3
3
  import { TOOLS, executeTool } from './tools.js';
4
4
  import { extractCommandFromText } from './intentDetect.js';
5
5
 
6
+ const sleep = ms => new Promise(r => setTimeout(r, ms));
7
+
6
8
  export function createChatState() {
7
9
  return {
8
10
  messages: [],
@@ -26,10 +28,23 @@ export async function handleSend(state, setState, input, model, provider) {
26
28
  setState(s => ({ ...s, messages: [...s.messages, userMsg], thinking: true, streamBuffer: '' }));
27
29
 
28
30
  try {
29
- const history = [...state.messages, userMsg].map(m => ({
30
- role: m.role === 'error' ? 'assistant' : m.role,
31
- content: m.content,
32
- }));
31
+ const history = [...state.messages, userMsg].map(m => {
32
+ const base = {
33
+ role: m.role === 'error' ? 'assistant' : m.role,
34
+ content: m.content,
35
+ };
36
+ if (m.role === 'tool' && m.tool_call_id) {
37
+ base.tool_call_id = m.tool_call_id;
38
+ }
39
+ if (m.role === 'assistant' && m.toolCalls) {
40
+ base.tool_calls = m.toolCalls.map(tc => ({
41
+ id: tc.id,
42
+ type: 'function',
43
+ function: { name: tc.function.name, arguments: tc.function.arguments },
44
+ }));
45
+ }
46
+ return base;
47
+ });
33
48
 
34
49
  await processStream(provider, model, history, state.agentMode, setState);
35
50
  } catch (err) {
@@ -50,48 +65,62 @@ async function processStream(provider, model, history, agentMode, setState, dept
50
65
  return;
51
66
  }
52
67
 
53
- const stream = callAI(provider, model, history, { tools: agentMode ? TOOLS : undefined });
68
+ let stream;
69
+ try {
70
+ stream = callAI(provider, model, history, { tools: agentMode ? TOOLS : undefined });
71
+ } catch (err) {
72
+ if (err.type === 'rate_limit') {
73
+ await sleep(2000);
74
+ return processStream(provider, model, history, agentMode, setState, depth);
75
+ }
76
+ throw err;
77
+ }
78
+
54
79
  let buffer = '';
55
80
  let toolCalls = null;
56
81
 
57
- for await (const event of stream) {
58
- if (event.type === 'token') {
59
- buffer += event.content;
60
- setState(s => ({
61
- ...s,
62
- streamBuffer: buffer,
63
- messages: updateLastAssistant(s.messages, buffer),
64
- }));
65
- } else if (event.type === 'tool_calls') {
66
- toolCalls = event.calls;
67
- } else if (event.type === 'error') {
68
- handleError(setState, event);
69
- return;
82
+ try {
83
+ for await (const event of stream) {
84
+ if (event.type === 'token') {
85
+ buffer += event.content;
86
+ setState(s => ({
87
+ ...s,
88
+ streamBuffer: buffer,
89
+ messages: updateLastAssistant(s.messages, buffer),
90
+ }));
91
+ } else if (event.type === 'tool_calls') {
92
+ toolCalls = event.calls;
93
+ } else if (event.type === 'error') {
94
+ handleError(setState, event);
95
+ return;
96
+ }
97
+ }
98
+ } catch (err) {
99
+ if (err.type === 'rate_limit') {
100
+ await sleep(2000);
101
+ return processStream(provider, model, history, agentMode, setState, depth);
70
102
  }
103
+ throw err;
71
104
  }
72
105
 
73
106
  if (toolCalls && toolCalls.length > 0 && agentMode) {
74
- const assistantMsg = { id: nextId(), role: 'assistant', content: buffer || '', toolCalls };
75
107
  const toolResults = [];
76
-
77
108
  for (const tc of toolCalls) {
78
109
  const { name, arguments: argsStr } = tc.function;
79
110
  let args;
80
- try {
81
- args = JSON.parse(argsStr);
82
- } catch {
83
- args = {};
84
- }
85
-
86
- setState(s => ({
87
- ...s,
88
- messages: [...s.messages, { id: nextId(), role: 'tool', content: '⚙ ' + name + '(' + JSON.stringify(args).slice(0, 100) + ')...', toolName: name }],
89
- }));
90
-
111
+ try { args = JSON.parse(argsStr); } catch { args = {}; }
91
112
  const result = await executeTool(name, args);
92
- toolResults.push({ tool_call_id: tc.id, role: 'tool', content: result });
113
+ toolResults.push({ tool_call_id: tc.id, role: 'tool', content: result, name });
93
114
  }
94
115
 
116
+ const newAssistantMsg = { id: nextId(), role: 'assistant', content: buffer || '', toolCalls };
117
+ const toolMsgs = toolResults.map(tr => ({
118
+ id: nextId(), role: 'tool', content: tr.content,
119
+ tool_call_id: tr.tool_call_id, toolName: tr.name,
120
+ }));
121
+
122
+ setState(s => ({ ...s, messages: [...s.messages, newAssistantMsg, ...toolMsgs] }));
123
+
95
124
  const newHistory = [
96
125
  ...history,
97
126
  { role: 'assistant', content: buffer || null, tool_calls: toolCalls.map(tc => ({