kimaki 0.4.4 → 0.4.6
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/cli.js +14 -1
- package/dist/discordBot.js +53 -11
- package/dist/tools.js +2 -1
- package/package.json +3 -3
- package/src/cli.ts +15 -1
- package/src/discordBot.ts +80 -28
- package/src/tools.ts +2 -1
package/dist/cli.js
CHANGED
|
@@ -7,10 +7,23 @@ import { Events, ChannelType, REST, Routes, SlashCommandBuilder, } from 'discord
|
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import fs from 'node:fs';
|
|
9
9
|
import { createLogger } from './logger.js';
|
|
10
|
-
import { spawnSync, execSync } from 'node:child_process';
|
|
10
|
+
import { spawn, spawnSync, execSync } from 'node:child_process';
|
|
11
11
|
const cliLogger = createLogger('CLI');
|
|
12
12
|
const cli = cac('kimaki');
|
|
13
13
|
process.title = 'kimaki';
|
|
14
|
+
process.on('SIGUSR2', () => {
|
|
15
|
+
cliLogger.info('Received SIGUSR2, restarting process in 1000ms...');
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
cliLogger.info('Restarting...');
|
|
18
|
+
spawn(process.argv[0], [...process.execArgv, ...process.argv.slice(1)], {
|
|
19
|
+
stdio: 'inherit',
|
|
20
|
+
detached: true,
|
|
21
|
+
cwd: process.cwd(),
|
|
22
|
+
env: process.env,
|
|
23
|
+
}).unref();
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}, 1000);
|
|
26
|
+
});
|
|
14
27
|
const EXIT_NO_RESTART = 64;
|
|
15
28
|
async function registerCommands(token, appId) {
|
|
16
29
|
const commands = [
|
package/dist/discordBot.js
CHANGED
|
@@ -595,6 +595,15 @@ async function processVoiceAttachment({ message, thread, projectDirectory, isNew
|
|
|
595
595
|
await sendThreadMessage(thread, `📝 **Transcribed message:** ${escapeDiscordFormatting(transcription)}`);
|
|
596
596
|
return transcription;
|
|
597
597
|
}
|
|
598
|
+
function getImageAttachments(message) {
|
|
599
|
+
const imageAttachments = Array.from(message.attachments.values()).filter((attachment) => attachment.contentType?.startsWith('image/'));
|
|
600
|
+
return imageAttachments.map((attachment) => ({
|
|
601
|
+
type: 'file',
|
|
602
|
+
mime: attachment.contentType || 'image/png',
|
|
603
|
+
filename: attachment.name,
|
|
604
|
+
url: attachment.url,
|
|
605
|
+
}));
|
|
606
|
+
}
|
|
598
607
|
export function escapeBackticksInCodeBlocks(markdown) {
|
|
599
608
|
const lexer = new Lexer();
|
|
600
609
|
const tokens = lexer.lex(markdown);
|
|
@@ -873,7 +882,7 @@ function formatPart(part) {
|
|
|
873
882
|
const outputToDisplay = getToolOutputToDisplay(part);
|
|
874
883
|
let toolTitle = part.state.status === 'completed' ? part.state.title || '' : 'error';
|
|
875
884
|
if (toolTitle) {
|
|
876
|
-
toolTitle =
|
|
885
|
+
toolTitle = `*${toolTitle}*`;
|
|
877
886
|
}
|
|
878
887
|
const icon = part.state.status === 'completed' ? '◼︎' : part.state.status === 'error' ? '⨯' : '';
|
|
879
888
|
const title = `${icon} ${part.tool} ${toolTitle} ${summaryText}`;
|
|
@@ -901,7 +910,7 @@ export async function createDiscordClient() {
|
|
|
901
910
|
],
|
|
902
911
|
});
|
|
903
912
|
}
|
|
904
|
-
async function handleOpencodeSession(prompt, thread, projectDirectory, originalMessage) {
|
|
913
|
+
async function handleOpencodeSession({ prompt, thread, projectDirectory, originalMessage, images = [], }) {
|
|
905
914
|
voiceLogger.log(`[OPENCODE SESSION] Starting for thread ${thread.id} with prompt: "${prompt.slice(0, 50)}${prompt.length > 50 ? '...' : ''}"`);
|
|
906
915
|
// Track session start time
|
|
907
916
|
const sessionStartTime = Date.now();
|
|
@@ -1211,12 +1220,17 @@ async function handleOpencodeSession(prompt, thread, projectDirectory, originalM
|
|
|
1211
1220
|
};
|
|
1212
1221
|
try {
|
|
1213
1222
|
voiceLogger.log(`[PROMPT] Sending prompt to session ${session.id}: "${prompt.slice(0, 100)}${prompt.length > 100 ? '...' : ''}"`);
|
|
1223
|
+
if (images.length > 0) {
|
|
1224
|
+
sessionLogger.log(`[PROMPT] Sending ${images.length} image(s):`, images.map((img) => ({ mime: img.mime, filename: img.filename, url: img.url.slice(0, 100) })));
|
|
1225
|
+
}
|
|
1214
1226
|
// Start the event handler
|
|
1215
1227
|
const eventHandlerPromise = eventHandler();
|
|
1228
|
+
const parts = [{ type: 'text', text: prompt }, ...images];
|
|
1229
|
+
sessionLogger.log(`[PROMPT] Parts to send:`, parts.length);
|
|
1216
1230
|
const response = await getClient().session.prompt({
|
|
1217
1231
|
path: { id: session.id },
|
|
1218
1232
|
body: {
|
|
1219
|
-
parts
|
|
1233
|
+
parts,
|
|
1220
1234
|
},
|
|
1221
1235
|
signal: abortController.signal,
|
|
1222
1236
|
});
|
|
@@ -1407,7 +1421,14 @@ export async function startDiscordBot({ token, appId, discordClient, }) {
|
|
|
1407
1421
|
if (transcription) {
|
|
1408
1422
|
messageContent = transcription;
|
|
1409
1423
|
}
|
|
1410
|
-
|
|
1424
|
+
const images = getImageAttachments(message);
|
|
1425
|
+
await handleOpencodeSession({
|
|
1426
|
+
prompt: messageContent,
|
|
1427
|
+
thread,
|
|
1428
|
+
projectDirectory,
|
|
1429
|
+
originalMessage: message,
|
|
1430
|
+
images,
|
|
1431
|
+
});
|
|
1411
1432
|
return;
|
|
1412
1433
|
}
|
|
1413
1434
|
// For text channels, start new sessions with kimaki.directory tag
|
|
@@ -1466,7 +1487,14 @@ export async function startDiscordBot({ token, appId, discordClient, }) {
|
|
|
1466
1487
|
if (transcription) {
|
|
1467
1488
|
messageContent = transcription;
|
|
1468
1489
|
}
|
|
1469
|
-
|
|
1490
|
+
const images = getImageAttachments(message);
|
|
1491
|
+
await handleOpencodeSession({
|
|
1492
|
+
prompt: messageContent,
|
|
1493
|
+
thread,
|
|
1494
|
+
projectDirectory,
|
|
1495
|
+
originalMessage: message,
|
|
1496
|
+
images,
|
|
1497
|
+
});
|
|
1470
1498
|
}
|
|
1471
1499
|
else {
|
|
1472
1500
|
discordLogger.log(`Channel type ${channel.type} is not supported`);
|
|
@@ -1721,7 +1749,11 @@ export async function startDiscordBot({ token, appId, discordClient, }) {
|
|
|
1721
1749
|
});
|
|
1722
1750
|
await command.editReply(`Created new session in ${thread.toString()}`);
|
|
1723
1751
|
// Start the OpenCode session
|
|
1724
|
-
await handleOpencodeSession(
|
|
1752
|
+
await handleOpencodeSession({
|
|
1753
|
+
prompt: fullPrompt,
|
|
1754
|
+
thread,
|
|
1755
|
+
projectDirectory,
|
|
1756
|
+
});
|
|
1725
1757
|
}
|
|
1726
1758
|
catch (error) {
|
|
1727
1759
|
voiceLogger.error('[SESSION] Error:', error);
|
|
@@ -1818,16 +1850,26 @@ export async function startDiscordBot({ token, appId, discordClient, }) {
|
|
|
1818
1850
|
}
|
|
1819
1851
|
else if (message.info.role === 'assistant') {
|
|
1820
1852
|
// Render assistant parts
|
|
1853
|
+
const partsToRender = [];
|
|
1821
1854
|
for (const part of message.parts) {
|
|
1822
1855
|
const content = formatPart(part);
|
|
1823
1856
|
if (content.trim()) {
|
|
1824
|
-
|
|
1825
|
-
// Store part-message mapping in database
|
|
1826
|
-
getDatabase()
|
|
1827
|
-
.prepare('INSERT OR REPLACE INTO part_messages (part_id, message_id, thread_id) VALUES (?, ?, ?)')
|
|
1828
|
-
.run(part.id, discordMessage.id, thread.id);
|
|
1857
|
+
partsToRender.push({ id: part.id, content });
|
|
1829
1858
|
}
|
|
1830
1859
|
}
|
|
1860
|
+
if (partsToRender.length > 0) {
|
|
1861
|
+
const combinedContent = partsToRender
|
|
1862
|
+
.map((p) => p.content)
|
|
1863
|
+
.join('\n\n');
|
|
1864
|
+
const discordMessage = await sendThreadMessage(thread, combinedContent);
|
|
1865
|
+
const stmt = getDatabase().prepare('INSERT OR REPLACE INTO part_messages (part_id, message_id, thread_id) VALUES (?, ?, ?)');
|
|
1866
|
+
const transaction = getDatabase().transaction((parts) => {
|
|
1867
|
+
for (const part of parts) {
|
|
1868
|
+
stmt.run(part.id, discordMessage.id, thread.id);
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
transaction(partsToRender);
|
|
1872
|
+
}
|
|
1831
1873
|
}
|
|
1832
1874
|
messageCount++;
|
|
1833
1875
|
}
|
package/dist/tools.js
CHANGED
|
@@ -95,7 +95,7 @@ export async function getTools({ onMessageCompleted, directory, }) {
|
|
|
95
95
|
.optional()
|
|
96
96
|
.describe('Optional model to use for this session'),
|
|
97
97
|
}),
|
|
98
|
-
execute: async ({ message, title,
|
|
98
|
+
execute: async ({ message, title, }) => {
|
|
99
99
|
if (!message.trim()) {
|
|
100
100
|
throw new Error(`message must be a non empty string`);
|
|
101
101
|
}
|
|
@@ -114,6 +114,7 @@ export async function getTools({ onMessageCompleted, directory, }) {
|
|
|
114
114
|
path: { id: session.data.id },
|
|
115
115
|
body: {
|
|
116
116
|
parts: [{ type: 'text', text: message }],
|
|
117
|
+
// model,
|
|
117
118
|
},
|
|
118
119
|
})
|
|
119
120
|
.then(async (response) => {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "kimaki",
|
|
3
3
|
"module": "index.ts",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.6",
|
|
6
6
|
"repository": "https://github.com/remorses/kimaki",
|
|
7
7
|
"bin": "bin.js",
|
|
8
8
|
"files": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@discordjs/opus": "^0.10.0",
|
|
25
25
|
"@discordjs/voice": "^0.19.0",
|
|
26
26
|
"@google/genai": "^1.16.0",
|
|
27
|
-
"@opencode-ai/sdk": "^0.
|
|
27
|
+
"@opencode-ai/sdk": "^1.0.115",
|
|
28
28
|
"@purinton/resampler": "^1.0.4",
|
|
29
29
|
"@snazzah/davey": "^0.1.6",
|
|
30
30
|
"ai": "^5.0.29",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"zod": "^4.0.17"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
|
-
"dev": "
|
|
48
|
+
"dev": "tsx --env-file .env src/cli.ts",
|
|
49
49
|
"dev:bun": "DEBUG=1 bun --env-file .env src/cli.ts",
|
|
50
50
|
"watch": "tsx scripts/watch-session.ts",
|
|
51
51
|
"test:events": "tsx test-events.ts",
|
package/src/cli.ts
CHANGED
|
@@ -37,13 +37,27 @@ import {
|
|
|
37
37
|
import path from 'node:path'
|
|
38
38
|
import fs from 'node:fs'
|
|
39
39
|
import { createLogger } from './logger.js'
|
|
40
|
-
import { spawnSync, execSync, type ExecSyncOptions } from 'node:child_process'
|
|
40
|
+
import { spawn, spawnSync, execSync, type ExecSyncOptions } from 'node:child_process'
|
|
41
41
|
|
|
42
42
|
const cliLogger = createLogger('CLI')
|
|
43
43
|
const cli = cac('kimaki')
|
|
44
44
|
|
|
45
45
|
process.title = 'kimaki'
|
|
46
46
|
|
|
47
|
+
process.on('SIGUSR2', () => {
|
|
48
|
+
cliLogger.info('Received SIGUSR2, restarting process in 1000ms...')
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
cliLogger.info('Restarting...')
|
|
51
|
+
spawn(process.argv[0]!, [...process.execArgv, ...process.argv.slice(1)], {
|
|
52
|
+
stdio: 'inherit',
|
|
53
|
+
detached: true,
|
|
54
|
+
cwd: process.cwd(),
|
|
55
|
+
env: process.env,
|
|
56
|
+
}).unref()
|
|
57
|
+
process.exit(0)
|
|
58
|
+
}, 1000)
|
|
59
|
+
})
|
|
60
|
+
|
|
47
61
|
const EXIT_NO_RESTART = 64
|
|
48
62
|
|
|
49
63
|
type Project = {
|
package/src/discordBot.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type OpencodeClient,
|
|
4
4
|
type Part,
|
|
5
5
|
type Config,
|
|
6
|
+
type FilePartInput,
|
|
6
7
|
} from '@opencode-ai/sdk'
|
|
7
8
|
|
|
8
9
|
import { createGenAIWorker, type GenAIWorker } from './genai-worker-wrapper.js'
|
|
@@ -813,6 +814,19 @@ async function processVoiceAttachment({
|
|
|
813
814
|
return transcription
|
|
814
815
|
}
|
|
815
816
|
|
|
817
|
+
function getImageAttachments(message: Message): FilePartInput[] {
|
|
818
|
+
const imageAttachments = Array.from(message.attachments.values()).filter(
|
|
819
|
+
(attachment) => attachment.contentType?.startsWith('image/'),
|
|
820
|
+
)
|
|
821
|
+
|
|
822
|
+
return imageAttachments.map((attachment) => ({
|
|
823
|
+
type: 'file' as const,
|
|
824
|
+
mime: attachment.contentType || 'image/png',
|
|
825
|
+
filename: attachment.name,
|
|
826
|
+
url: attachment.url,
|
|
827
|
+
}))
|
|
828
|
+
}
|
|
829
|
+
|
|
816
830
|
export function escapeBackticksInCodeBlocks(markdown: string): string {
|
|
817
831
|
const lexer = new Lexer()
|
|
818
832
|
const tokens = lexer.lex(markdown)
|
|
@@ -1160,7 +1174,7 @@ function formatPart(part: Part): string {
|
|
|
1160
1174
|
|
|
1161
1175
|
let toolTitle = part.state.status === 'completed' ? part.state.title || '' : 'error'
|
|
1162
1176
|
if (toolTitle) {
|
|
1163
|
-
toolTitle =
|
|
1177
|
+
toolTitle = `*${toolTitle}*`
|
|
1164
1178
|
}
|
|
1165
1179
|
|
|
1166
1180
|
const icon = part.state.status === 'completed' ? '◼︎' : part.state.status === 'error' ? '⨯' : ''
|
|
@@ -1193,12 +1207,19 @@ export async function createDiscordClient() {
|
|
|
1193
1207
|
})
|
|
1194
1208
|
}
|
|
1195
1209
|
|
|
1196
|
-
async function handleOpencodeSession(
|
|
1197
|
-
prompt
|
|
1198
|
-
thread
|
|
1199
|
-
projectDirectory
|
|
1200
|
-
originalMessage
|
|
1201
|
-
|
|
1210
|
+
async function handleOpencodeSession({
|
|
1211
|
+
prompt,
|
|
1212
|
+
thread,
|
|
1213
|
+
projectDirectory,
|
|
1214
|
+
originalMessage,
|
|
1215
|
+
images = [],
|
|
1216
|
+
}: {
|
|
1217
|
+
prompt: string
|
|
1218
|
+
thread: ThreadChannel
|
|
1219
|
+
projectDirectory?: string
|
|
1220
|
+
originalMessage?: Message
|
|
1221
|
+
images?: FilePartInput[]
|
|
1222
|
+
}): Promise<{ sessionID: string; result: any; port?: number } | undefined> {
|
|
1202
1223
|
voiceLogger.log(
|
|
1203
1224
|
`[OPENCODE SESSION] Starting for thread ${thread.id} with prompt: "${prompt.slice(0, 50)}${prompt.length > 50 ? '...' : ''}"`,
|
|
1204
1225
|
)
|
|
@@ -1600,14 +1621,20 @@ async function handleOpencodeSession(
|
|
|
1600
1621
|
voiceLogger.log(
|
|
1601
1622
|
`[PROMPT] Sending prompt to session ${session.id}: "${prompt.slice(0, 100)}${prompt.length > 100 ? '...' : ''}"`,
|
|
1602
1623
|
)
|
|
1624
|
+
if (images.length > 0) {
|
|
1625
|
+
sessionLogger.log(`[PROMPT] Sending ${images.length} image(s):`, images.map((img) => ({ mime: img.mime, filename: img.filename, url: img.url.slice(0, 100) })))
|
|
1626
|
+
}
|
|
1603
1627
|
|
|
1604
1628
|
// Start the event handler
|
|
1605
1629
|
const eventHandlerPromise = eventHandler()
|
|
1606
1630
|
|
|
1631
|
+
const parts = [{ type: 'text' as const, text: prompt }, ...images]
|
|
1632
|
+
sessionLogger.log(`[PROMPT] Parts to send:`, parts.length)
|
|
1633
|
+
|
|
1607
1634
|
const response = await getClient().session.prompt({
|
|
1608
1635
|
path: { id: session.id },
|
|
1609
1636
|
body: {
|
|
1610
|
-
parts
|
|
1637
|
+
parts,
|
|
1611
1638
|
},
|
|
1612
1639
|
signal: abortController.signal,
|
|
1613
1640
|
})
|
|
@@ -1874,12 +1901,14 @@ export async function startDiscordBot({
|
|
|
1874
1901
|
messageContent = transcription
|
|
1875
1902
|
}
|
|
1876
1903
|
|
|
1877
|
-
|
|
1878
|
-
|
|
1904
|
+
const images = getImageAttachments(message)
|
|
1905
|
+
await handleOpencodeSession({
|
|
1906
|
+
prompt: messageContent,
|
|
1879
1907
|
thread,
|
|
1880
1908
|
projectDirectory,
|
|
1881
|
-
message,
|
|
1882
|
-
|
|
1909
|
+
originalMessage: message,
|
|
1910
|
+
images,
|
|
1911
|
+
})
|
|
1883
1912
|
return
|
|
1884
1913
|
}
|
|
1885
1914
|
|
|
@@ -1967,12 +1996,14 @@ export async function startDiscordBot({
|
|
|
1967
1996
|
messageContent = transcription
|
|
1968
1997
|
}
|
|
1969
1998
|
|
|
1970
|
-
|
|
1971
|
-
|
|
1999
|
+
const images = getImageAttachments(message)
|
|
2000
|
+
await handleOpencodeSession({
|
|
2001
|
+
prompt: messageContent,
|
|
1972
2002
|
thread,
|
|
1973
2003
|
projectDirectory,
|
|
1974
|
-
message,
|
|
1975
|
-
|
|
2004
|
+
originalMessage: message,
|
|
2005
|
+
images,
|
|
2006
|
+
})
|
|
1976
2007
|
} else {
|
|
1977
2008
|
discordLogger.log(`Channel type ${channel.type} is not supported`)
|
|
1978
2009
|
}
|
|
@@ -2307,7 +2338,11 @@ export async function startDiscordBot({
|
|
|
2307
2338
|
)
|
|
2308
2339
|
|
|
2309
2340
|
// Start the OpenCode session
|
|
2310
|
-
await handleOpencodeSession(
|
|
2341
|
+
await handleOpencodeSession({
|
|
2342
|
+
prompt: fullPrompt,
|
|
2343
|
+
thread,
|
|
2344
|
+
projectDirectory,
|
|
2345
|
+
})
|
|
2311
2346
|
} catch (error) {
|
|
2312
2347
|
voiceLogger.error('[SESSION] Error:', error)
|
|
2313
2348
|
await command.editReply(
|
|
@@ -2446,22 +2481,39 @@ export async function startDiscordBot({
|
|
|
2446
2481
|
}
|
|
2447
2482
|
} else if (message.info.role === 'assistant') {
|
|
2448
2483
|
// Render assistant parts
|
|
2484
|
+
const partsToRender: { id: string; content: string }[] = []
|
|
2485
|
+
|
|
2449
2486
|
for (const part of message.parts) {
|
|
2450
2487
|
const content = formatPart(part)
|
|
2451
2488
|
if (content.trim()) {
|
|
2452
|
-
|
|
2453
|
-
thread,
|
|
2454
|
-
content,
|
|
2455
|
-
)
|
|
2456
|
-
|
|
2457
|
-
// Store part-message mapping in database
|
|
2458
|
-
getDatabase()
|
|
2459
|
-
.prepare(
|
|
2460
|
-
'INSERT OR REPLACE INTO part_messages (part_id, message_id, thread_id) VALUES (?, ?, ?)',
|
|
2461
|
-
)
|
|
2462
|
-
.run(part.id, discordMessage.id, thread.id)
|
|
2489
|
+
partsToRender.push({ id: part.id, content })
|
|
2463
2490
|
}
|
|
2464
2491
|
}
|
|
2492
|
+
|
|
2493
|
+
if (partsToRender.length > 0) {
|
|
2494
|
+
const combinedContent = partsToRender
|
|
2495
|
+
.map((p) => p.content)
|
|
2496
|
+
.join('\n\n')
|
|
2497
|
+
|
|
2498
|
+
const discordMessage = await sendThreadMessage(
|
|
2499
|
+
thread,
|
|
2500
|
+
combinedContent,
|
|
2501
|
+
)
|
|
2502
|
+
|
|
2503
|
+
const stmt = getDatabase().prepare(
|
|
2504
|
+
'INSERT OR REPLACE INTO part_messages (part_id, message_id, thread_id) VALUES (?, ?, ?)',
|
|
2505
|
+
)
|
|
2506
|
+
|
|
2507
|
+
const transaction = getDatabase().transaction(
|
|
2508
|
+
(parts: { id: string }[]) => {
|
|
2509
|
+
for (const part of parts) {
|
|
2510
|
+
stmt.run(part.id, discordMessage.id, thread.id)
|
|
2511
|
+
}
|
|
2512
|
+
},
|
|
2513
|
+
)
|
|
2514
|
+
|
|
2515
|
+
transaction(partsToRender)
|
|
2516
|
+
}
|
|
2465
2517
|
}
|
|
2466
2518
|
messageCount++
|
|
2467
2519
|
}
|
package/src/tools.ts
CHANGED
|
@@ -127,7 +127,7 @@ export async function getTools({
|
|
|
127
127
|
.optional()
|
|
128
128
|
.describe('Optional model to use for this session'),
|
|
129
129
|
}),
|
|
130
|
-
execute: async ({ message, title,
|
|
130
|
+
execute: async ({ message, title, }) => {
|
|
131
131
|
if (!message.trim()) {
|
|
132
132
|
throw new Error(`message must be a non empty string`)
|
|
133
133
|
}
|
|
@@ -149,6 +149,7 @@ export async function getTools({
|
|
|
149
149
|
path: { id: session.data.id },
|
|
150
150
|
body: {
|
|
151
151
|
parts: [{ type: 'text', text: message }],
|
|
152
|
+
// model,
|
|
152
153
|
},
|
|
153
154
|
})
|
|
154
155
|
.then(async (response) => {
|