tarsk 0.2.0 → 0.2.2
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/agent/agent.js +1 -131
- package/dist/agent/interfaces.js +1 -1
- package/dist/api/encryption.js +1 -53
- package/dist/api/models.js +1 -162
- package/dist/api/prompt.js +1 -12
- package/dist/api/settings.js +1 -41
- package/dist/api/test.js +1 -30
- package/dist/api/tools.js +1 -313
- package/dist/api/utils.js +1 -18
- package/dist/index.js +1 -40
- package/dist/interfaces/meta.js +1 -1
- package/dist/interfaces/model.js +1 -1
- package/dist/interfaces/settings.js +1 -1
- package/dist/log/log.js +1 -35
- package/dist/prompt.js +1 -52
- package/dist/tools/books.js +1 -39
- package/dist/tools.js +1 -91
- package/dist/utils/files.js +1 -14
- package/dist/utils/json-file.js +1 -29
- package/dist/utils/strip-markdown.js +1 -7
- package/package.json +1 -2
package/dist/agent/agent.js
CHANGED
|
@@ -1,131 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export async function completion(request, options) {
|
|
3
|
-
// Get the full list of tool definitions
|
|
4
|
-
const tools = request.tools.map(tool => JSON.parse(JSON.stringify(tool.definition)) ?? []);
|
|
5
|
-
// Create the object without the functions etc of the request
|
|
6
|
-
const req = {
|
|
7
|
-
model: request.model,
|
|
8
|
-
tools,
|
|
9
|
-
messages: request.messages,
|
|
10
|
-
};
|
|
11
|
-
logStart({ type: 'completion', args: '' });
|
|
12
|
-
let repeating;
|
|
13
|
-
let count = 0;
|
|
14
|
-
let response = {};
|
|
15
|
-
do {
|
|
16
|
-
repeating = false;
|
|
17
|
-
count++;
|
|
18
|
-
logStart({ type: `completion_request_${count}`, args: request });
|
|
19
|
-
response = await call(options, req);
|
|
20
|
-
logEnd(`completion_request_${count}`);
|
|
21
|
-
if (response.error) {
|
|
22
|
-
const message = JSON.stringify(response.error.metadata);
|
|
23
|
-
logError({ type: 'completion_error', message, args: { ...response.error } });
|
|
24
|
-
throw new Error(response.error.message);
|
|
25
|
-
}
|
|
26
|
-
for (const choice of response.choices) {
|
|
27
|
-
// Inject the response message back into the request so the LLM knows for next round
|
|
28
|
-
logWrite({ type: 'inject_choice', args: choice });
|
|
29
|
-
request.messages.push(cleanedMessage(choice.message));
|
|
30
|
-
if (choice.finish_reason == 'tool_calls') {
|
|
31
|
-
logStart({ type: 'tool_calls', args: choice.message.tool_calls });
|
|
32
|
-
const newMessages = await callTools(choice.message.tool_calls ?? [], request);
|
|
33
|
-
request.messages.push(...newMessages);
|
|
34
|
-
logEnd('tool_calls');
|
|
35
|
-
// Do another loop if requested
|
|
36
|
-
repeating = newMessages.length > 0;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
} while (repeating);
|
|
40
|
-
logEnd('completion');
|
|
41
|
-
return response;
|
|
42
|
-
}
|
|
43
|
-
// This gets rid of the fields that the LLM throws errors on
|
|
44
|
-
function cleanedMessage(message) {
|
|
45
|
-
const cleaned = JSON.parse(JSON.stringify(message));
|
|
46
|
-
delete (cleaned.reasoning);
|
|
47
|
-
delete (cleaned.refusal);
|
|
48
|
-
for (const call of cleaned.tool_calls ?? []) {
|
|
49
|
-
delete (call.index);
|
|
50
|
-
}
|
|
51
|
-
return cleaned;
|
|
52
|
-
}
|
|
53
|
-
export async function callTools(toolCalls, request) {
|
|
54
|
-
const newMessages = [];
|
|
55
|
-
for (const toolCall of toolCalls) {
|
|
56
|
-
const toolName = toolCall.function.name;
|
|
57
|
-
const toolArgs = JSON.parse(toolCall.function.arguments);
|
|
58
|
-
// Find the tool by function name
|
|
59
|
-
let found;
|
|
60
|
-
let functionMap;
|
|
61
|
-
for (const tool of request.tools) {
|
|
62
|
-
for (const definition of tool.definition ?? []) {
|
|
63
|
-
if (definition.function.name == toolName) {
|
|
64
|
-
found = definition;
|
|
65
|
-
functionMap = tool.functionMap;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (!found) {
|
|
71
|
-
throw new Error(`Tool ${toolName} not found`);
|
|
72
|
-
}
|
|
73
|
-
if (!functionMap) {
|
|
74
|
-
throw new Error(`Tool map for ${toolName} not found`);
|
|
75
|
-
}
|
|
76
|
-
// TODO: Promise all the tool calls
|
|
77
|
-
if (!functionMap[toolName]) {
|
|
78
|
-
throw new Error(`Tool ${toolName} not found in tool map. Check the function name in the definition matches exactly to the name of the function`);
|
|
79
|
-
}
|
|
80
|
-
logStart({ type: `tool_call_${toolName}`, args: { toolName, toolArgs } });
|
|
81
|
-
logWrite({ type: 'tool_call', args: { functionMap, toolArgs } });
|
|
82
|
-
try {
|
|
83
|
-
const toolResponse = await functionMap[toolName](...Object.values(toolArgs));
|
|
84
|
-
newMessages.push({
|
|
85
|
-
role: 'tool',
|
|
86
|
-
tool_call_id: toolCall.id,
|
|
87
|
-
//toolCallId: toolCall.id,
|
|
88
|
-
name: toolName,
|
|
89
|
-
content: JSON.stringify(toolResponse),
|
|
90
|
-
});
|
|
91
|
-
logWrite({ type: 'tool_call_response', args: { toolName, toolResponse }, message: JSON.stringify(toolResponse) });
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
var err = new Error();
|
|
95
|
-
logWrite({ type: 'tool_call_error', args: { toolName, error }, message: err.stack?.toString() });
|
|
96
|
-
throw new Error(`Error calling tool ${toolName}: ${error}`);
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
logEnd(`tool_call_${toolName}`);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return newMessages;
|
|
103
|
-
}
|
|
104
|
-
export async function callFunction(_function, args) {
|
|
105
|
-
try {
|
|
106
|
-
const toolResponse = await _function(args);
|
|
107
|
-
return toolResponse;
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
var err = new Error();
|
|
111
|
-
logWrite({ type: 'test_call_error', args: { functionName: _function.name, error: `${error}` },
|
|
112
|
-
// message: err.stack?.toString()
|
|
113
|
-
});
|
|
114
|
-
return { error: `Error calling ${_function.name}: ${error}` };
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
async function call(options, request) {
|
|
118
|
-
const response = await fetch(`${options.baseUrl}/chat/completions`, {
|
|
119
|
-
method: 'POST',
|
|
120
|
-
headers: {
|
|
121
|
-
'Authorization': 'Bearer ' + options.apiKey,
|
|
122
|
-
'HTTP-Referer': 'https://tarsk.io', // Optional. Site URL for rankings on openrouter.ai.
|
|
123
|
-
'X-Title': 'Tarsk', // Optional. Site title for rankings on openrouter.ai.
|
|
124
|
-
'Content-Type': 'application/json',
|
|
125
|
-
},
|
|
126
|
-
body: JSON.stringify(request)
|
|
127
|
-
});
|
|
128
|
-
const text = await response.text();
|
|
129
|
-
const json = JSON.parse(text);
|
|
130
|
-
return json;
|
|
131
|
-
}
|
|
1
|
+
import{logWrite,logEnd,logError,logStart}from"../log/log.js";export async function completion(o,t){const e=o.tools.map((o=>JSON.parse(JSON.stringify(o.definition))??[])),r={model:o.model,tools:e,messages:o.messages};let n;logStart({type:"completion",args:""});let l=0,a={};do{if(n=!1,l++,logStart({type:`completion_request_${l}`,args:o}),a=await call(t,r),logEnd(`completion_request_${l}`),a.error){const o=JSON.stringify(a.error.metadata);throw logError({type:"completion_error",message:o,args:{...a.error}}),new Error(a.error.message)}for(const t of a.choices)if(logWrite({type:"inject_choice",args:t}),o.messages.push(cleanedMessage(t.message)),"tool_calls"==t.finish_reason){logStart({type:"tool_calls",args:t.message.tool_calls});const e=await callTools(t.message.tool_calls??[],o);o.messages.push(...e),logEnd("tool_calls"),n=e.length>0}}while(n);return logEnd("completion"),a}function cleanedMessage(o){const t=JSON.parse(JSON.stringify(o));delete t.reasoning,delete t.refusal;for(const o of t.tool_calls??[])delete o.index;return t}export async function callTools(o,t){const e=[];for(const n of o){const o=n.function.name,l=JSON.parse(n.function.arguments);let a,s;for(const e of t.tools)for(const t of e.definition??[])if(t.function.name==o){a=t,s=e.functionMap;break}if(!a)throw new Error(`Tool ${o} not found`);if(!s)throw new Error(`Tool map for ${o} not found`);if(!s[o])throw new Error(`Tool ${o} not found in tool map. Check the function name in the definition matches exactly to the name of the function`);logStart({type:`tool_call_${o}`,args:{toolName:o,toolArgs:l}}),logWrite({type:"tool_call",args:{functionMap:s,toolArgs:l}});try{const t=await s[o](...Object.values(l));e.push({role:"tool",tool_call_id:n.id,name:o,content:JSON.stringify(t)}),logWrite({type:"tool_call_response",args:{toolName:o,toolResponse:t},message:JSON.stringify(t)})}catch(t){var r=new Error;throw logWrite({type:"tool_call_error",args:{toolName:o,error:t},message:r.stack?.toString()}),new Error(`Error calling tool ${o}: ${t}`)}finally{logEnd(`tool_call_${o}`)}}return e}export async function callFunction(o,t){try{return await o(t)}catch(t){new Error;return logWrite({type:"test_call_error",args:{functionName:o.name,error:`${t}`}}),{error:`Error calling ${o.name}: ${t}`}}}async function call(o,t){const e=await fetch(`${o.baseUrl}/chat/completions`,{method:"POST",headers:{Authorization:"Bearer "+o.apiKey,"HTTP-Referer":"https://tarsk.io","X-Title":"Tarsk","Content-Type":"application/json"},body:JSON.stringify(t)}),r=await e.text();return JSON.parse(r)}
|
package/dist/agent/interfaces.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{};
|
package/dist/api/encryption.js
CHANGED
|
@@ -1,53 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { hostname, machine } from "os";
|
|
3
|
-
export function encryptString(text) {
|
|
4
|
-
let id = `${machineIdSync()}11de20da-f6cf-4b85-b630-555fc6e8b881`;
|
|
5
|
-
return encrypt(text, id);
|
|
6
|
-
}
|
|
7
|
-
export function decryptString(text) {
|
|
8
|
-
try {
|
|
9
|
-
let id = `${machineIdSync()}11de20da-f6cf-4b85-b630-555fc6e8b881`;
|
|
10
|
-
return decrypt(text, id);
|
|
11
|
-
}
|
|
12
|
-
catch (e) {
|
|
13
|
-
return "";
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
function machineIdSync() {
|
|
17
|
-
return machine() + hostname();
|
|
18
|
-
}
|
|
19
|
-
const algorithm = "aes-256-cbc";
|
|
20
|
-
const ivLength = 16; // AES block size in bytes
|
|
21
|
-
/**
|
|
22
|
-
* Encrypts a string using AES-256-CBC and returns base64-encoded result.
|
|
23
|
-
* @param text - The plain text to encrypt.
|
|
24
|
-
* @param passPhrase - The secret passphrase used to generate the encryption key.
|
|
25
|
-
* @returns A base64 string containing IV and encrypted data, separated by a colon.
|
|
26
|
-
*/
|
|
27
|
-
function encrypt(text, passPhrase) {
|
|
28
|
-
const iv = randomBytes(ivLength);
|
|
29
|
-
const key = scryptSync(passPhrase, "salt", 32); // 256-bit key
|
|
30
|
-
const cipher = createCipheriv(algorithm, key, iv);
|
|
31
|
-
let encrypted = cipher.update(text, "utf8", "base64");
|
|
32
|
-
encrypted += cipher.final("base64");
|
|
33
|
-
const encryptedData = `${iv.toString("base64")}:${encrypted}`;
|
|
34
|
-
return encryptedData;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Decrypts a base64-encoded string using AES-256-CBC.
|
|
38
|
-
* @param encryptedData - The base64-encoded encrypted data (with IV).
|
|
39
|
-
* @param passPhrase - The secret passphrase used to generate the decryption key.
|
|
40
|
-
* @returns The decrypted plain text.
|
|
41
|
-
*/
|
|
42
|
-
function decrypt(encryptedData, passPhrase) {
|
|
43
|
-
const [ivBase64, encryptedBase64] = encryptedData.split(":");
|
|
44
|
-
if (!ivBase64 || !encryptedBase64) {
|
|
45
|
-
throw new Error("Invalid encrypted data format");
|
|
46
|
-
}
|
|
47
|
-
const iv = Buffer.from(ivBase64, "base64");
|
|
48
|
-
const key = scryptSync(passPhrase, "salt", 32);
|
|
49
|
-
const decipher = createDecipheriv(algorithm, key, iv);
|
|
50
|
-
let decrypted = decipher.update(encryptedBase64, "base64", "utf8");
|
|
51
|
-
decrypted += decipher.final("utf8");
|
|
52
|
-
return decrypted;
|
|
53
|
-
}
|
|
1
|
+
import{createCipheriv,createDecipheriv,randomBytes,scryptSync}from"crypto";import{hostname,machine}from"os";export function encryptString(t){return encrypt(t,`${machineIdSync()}11de20da-f6cf-4b85-b630-555fc6e8b881`)}export function decryptString(t){try{return decrypt(t,`${machineIdSync()}11de20da-f6cf-4b85-b630-555fc6e8b881`)}catch(t){return""}}function machineIdSync(){return machine()+hostname()}const algorithm="aes-256-cbc",ivLength=16;function encrypt(t,e){const r=randomBytes(ivLength),n=scryptSync(e,"salt",32),c=createCipheriv(algorithm,n,r);let a=c.update(t,"utf8","base64");a+=c.final("base64");return`${r.toString("base64")}:${a}`}function decrypt(t,e){const[r,n]=t.split(":");if(!r||!n)throw new Error("Invalid encrypted data format");const c=Buffer.from(r,"base64"),a=scryptSync(e,"salt",32),i=createDecipheriv(algorithm,a,c);let o=i.update(n,"base64","utf8");return o+=i.final("utf8"),o}
|
package/dist/api/models.js
CHANGED
|
@@ -1,162 +1 @@
|
|
|
1
|
-
export async function api_get_models(
|
|
2
|
-
const res = await fetch("https://openrouter.ai/api/v1/models");
|
|
3
|
-
const data = await res.json();
|
|
4
|
-
const models = [];
|
|
5
|
-
for (const model of data.data) {
|
|
6
|
-
if (hasTools(model.id)) {
|
|
7
|
-
models.push({
|
|
8
|
-
id: model.id,
|
|
9
|
-
description: model.description,
|
|
10
|
-
name: model.name,
|
|
11
|
-
type: model.architecture.tokenizer,
|
|
12
|
-
priceM: totalPrice(model.pricing),
|
|
13
|
-
price: model.pricing.prompt == "0" && model.pricing.completion == "0"
|
|
14
|
-
? ""
|
|
15
|
-
: `($${totalPrice(model.pricing).toFixed(2)}m)`,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return c.json(models);
|
|
20
|
-
}
|
|
21
|
-
function totalPrice(price) {
|
|
22
|
-
const total = v(price.prompt) + v(price.completion);
|
|
23
|
-
return total * 1000000;
|
|
24
|
-
}
|
|
25
|
-
function promptPrice(price) {
|
|
26
|
-
const total = v(price.prompt);
|
|
27
|
-
return total * 1000000;
|
|
28
|
-
}
|
|
29
|
-
function completionPrice(price) {
|
|
30
|
-
const total = v(price.completion);
|
|
31
|
-
return total * 1000000;
|
|
32
|
-
}
|
|
33
|
-
function v(s) {
|
|
34
|
-
if (!s)
|
|
35
|
-
return 0;
|
|
36
|
-
return Number(s);
|
|
37
|
-
}
|
|
38
|
-
function hasTools(model) {
|
|
39
|
-
return [
|
|
40
|
-
"google/gemini-2.5-pro-preview-03-25",
|
|
41
|
-
"google/gemini-2.5-flash-preview",
|
|
42
|
-
"google/gemini-2.5-flash-preview:thinking",
|
|
43
|
-
"openai/o4-mini-high",
|
|
44
|
-
"openai/o3",
|
|
45
|
-
"openai/o4-mini",
|
|
46
|
-
"openai/gpt-4.1",
|
|
47
|
-
"openai/gpt-4.1-mini",
|
|
48
|
-
"openai/gpt-4.1-nano",
|
|
49
|
-
"x-ai/grok-3-beta",
|
|
50
|
-
"meta-llama/llama-4-maverick",
|
|
51
|
-
"meta-llama/llama-4-scout",
|
|
52
|
-
"all-hands/openhands-lm-32b-v0.1",
|
|
53
|
-
"mistral/ministral-8b",
|
|
54
|
-
"google/gemini-2.5-pro-exp-03-25:free",
|
|
55
|
-
"deepseek/deepseek-chat-v3-0324",
|
|
56
|
-
"mistralai/mistral-small-3.1-24b-instruct:free",
|
|
57
|
-
"mistralai/mistral-small-3.1-24b-instruct",
|
|
58
|
-
"ai21/jamba-1.6-large",
|
|
59
|
-
"ai21/jamba-1.6-mini",
|
|
60
|
-
"qwen/qwq-32b",
|
|
61
|
-
"openai/gpt-4.5-preview",
|
|
62
|
-
"google/gemini-2.0-flash-lite-001",
|
|
63
|
-
"anthropic/claude-3.7-sonnet",
|
|
64
|
-
"anthropic/claude-3.7-sonnet:thinking",
|
|
65
|
-
"anthropic/claude-3.7-sonnet:beta",
|
|
66
|
-
"mistralai/mistral-saba",
|
|
67
|
-
"openai/o3-mini-high",
|
|
68
|
-
"google/gemini-2.0-flash-001",
|
|
69
|
-
"qwen/qwen-turbo",
|
|
70
|
-
"qwen/qwen-plus",
|
|
71
|
-
"qwen/qwen-max",
|
|
72
|
-
"openai/o3-mini",
|
|
73
|
-
"mistralai/mistral-small-24b-instruct-2501",
|
|
74
|
-
"deepseek/deepseek-r1-distill-llama-70b",
|
|
75
|
-
"deepseek/deepseek-r1",
|
|
76
|
-
"mistralai/codestral-2501",
|
|
77
|
-
"deepseek/deepseek-chat",
|
|
78
|
-
"openai/o1",
|
|
79
|
-
"x-ai/grok-2-1212",
|
|
80
|
-
"google/gemini-2.0-flash-exp:free",
|
|
81
|
-
"meta-llama/llama-3.3-70b-instruct",
|
|
82
|
-
"amazon/nova-lite-v1",
|
|
83
|
-
"amazon/nova-micro-v1",
|
|
84
|
-
"amazon/nova-pro-v1",
|
|
85
|
-
"openai/gpt-4o-2024-11-20",
|
|
86
|
-
"mistralai/mistral-large-2411",
|
|
87
|
-
"mistralai/mistral-large-2407",
|
|
88
|
-
"mistralai/pixtral-large-2411",
|
|
89
|
-
"anthropic/claude-3.5-haiku:beta",
|
|
90
|
-
"anthropic/claude-3.5-haiku",
|
|
91
|
-
"anthropic/claude-3.5-haiku-20241022:beta",
|
|
92
|
-
"anthropic/claude-3.5-haiku-20241022",
|
|
93
|
-
"anthropic/claude-3.5-sonnet:beta",
|
|
94
|
-
"anthropic/claude-3.5-sonnet",
|
|
95
|
-
"x-ai/grok-beta",
|
|
96
|
-
"mistralai/ministral-8b",
|
|
97
|
-
"mistralai/ministral-3b",
|
|
98
|
-
"nvidia/llama-3.1-nemotron-70b-instruct",
|
|
99
|
-
"google/gemini-flash-1.5-8b",
|
|
100
|
-
"meta-llama/llama-3.2-3b-instruct",
|
|
101
|
-
"qwen/qwen-2.5-72b-instruct",
|
|
102
|
-
"mistralai/pixtral-12b",
|
|
103
|
-
"cohere/command-r-plus-08-2024",
|
|
104
|
-
"cohere/command-r-08-2024",
|
|
105
|
-
"google/gemini-flash-1.5-8b-exp",
|
|
106
|
-
"ai21/jamba-1-5-mini",
|
|
107
|
-
"ai21/jamba-1-5-large",
|
|
108
|
-
"microsoft/phi-3.5-mini-128k-instruct",
|
|
109
|
-
"nousresearch/hermes-3-llama-3.1-70b",
|
|
110
|
-
"openai/gpt-4o-2024-08-06",
|
|
111
|
-
"meta-llama/llama-3.1-8b-instruct",
|
|
112
|
-
"meta-llama/llama-3.1-405b-instruct",
|
|
113
|
-
"meta-llama/llama-3.1-70b-instruct",
|
|
114
|
-
"mistralai/codestral-mamba",
|
|
115
|
-
"mistralai/mistral-nemo",
|
|
116
|
-
"openai/gpt-4o-mini",
|
|
117
|
-
"openai/gpt-4o-mini-2024-07-18",
|
|
118
|
-
"anthropic/claude-3.5-sonnet-20240620:beta",
|
|
119
|
-
"anthropic/claude-3.5-sonnet-20240620",
|
|
120
|
-
"mistralai/mistral-7b-instruct:free",
|
|
121
|
-
"mistralai/mistral-7b-instruct",
|
|
122
|
-
"mistralai/mistral-7b-instruct-v0.3",
|
|
123
|
-
"microsoft/phi-3-mini-128k-instruct",
|
|
124
|
-
"microsoft/phi-3-medium-128k-instruct",
|
|
125
|
-
"google/gemini-flash-1.5",
|
|
126
|
-
"openai/gpt-4o",
|
|
127
|
-
"openai/gpt-4o:extended",
|
|
128
|
-
"openai/gpt-4o-2024-05-13",
|
|
129
|
-
"meta-llama/llama-3-8b-instruct",
|
|
130
|
-
"meta-llama/llama-3-70b-instruct",
|
|
131
|
-
"mistralai/mixtral-8x22b-instruct",
|
|
132
|
-
"google/gemini-pro-1.5",
|
|
133
|
-
"openai/gpt-4-turbo",
|
|
134
|
-
"cohere/command-r-plus",
|
|
135
|
-
"cohere/command-r-plus-04-2024",
|
|
136
|
-
"cohere/command-r",
|
|
137
|
-
"anthropic/claude-3-haiku:beta",
|
|
138
|
-
"anthropic/claude-3-haiku",
|
|
139
|
-
"anthropic/claude-3-opus:beta",
|
|
140
|
-
"anthropic/claude-3-opus",
|
|
141
|
-
"anthropic/claude-3-sonnet:beta",
|
|
142
|
-
"anthropic/claude-3-sonnet",
|
|
143
|
-
"cohere/command-r-03-2024",
|
|
144
|
-
"mistralai/mistral-large",
|
|
145
|
-
"openai/gpt-3.5-turbo-0613",
|
|
146
|
-
"openai/gpt-4-turbo-preview",
|
|
147
|
-
"mistralai/mistral-medium",
|
|
148
|
-
"mistralai/mistral-small",
|
|
149
|
-
"mistralai/mistral-tiny",
|
|
150
|
-
"mistralai/mixtral-8x7b-instruct",
|
|
151
|
-
"openai/gpt-3.5-turbo-1106",
|
|
152
|
-
"openai/gpt-4-1106-preview",
|
|
153
|
-
"mistralai/mistral-7b-instruct-v0.1",
|
|
154
|
-
"openai/gpt-3.5-turbo-16k",
|
|
155
|
-
"openai/gpt-4-32k",
|
|
156
|
-
"openai/gpt-4-32k-0314",
|
|
157
|
-
"openai/gpt-3.5-turbo",
|
|
158
|
-
"openai/gpt-3.5-turbo-0125",
|
|
159
|
-
"openai/gpt-4",
|
|
160
|
-
"openai/gpt-4-0314",
|
|
161
|
-
].includes(model);
|
|
162
|
-
}
|
|
1
|
+
export async function api_get_models(a){const i=await fetch("https://openrouter.ai/api/v1/models"),e=await i.json(),t=[];for(const a of e.data)hasTools(a.id)&&t.push({id:a.id,description:a.description,name:a.name,type:a.architecture.tokenizer,priceM:totalPrice(a.pricing),price:"0"==a.pricing.prompt&&"0"==a.pricing.completion?"":`($${totalPrice(a.pricing).toFixed(2)}m)`});return a.json(t)}function totalPrice(a){return 1e6*(v(a.prompt)+v(a.completion))}function promptPrice(a){return 1e6*v(a.prompt)}function completionPrice(a){return 1e6*v(a.completion)}function v(a){return a?Number(a):0}function hasTools(a){return["google/gemini-2.5-pro-preview-03-25","google/gemini-2.5-flash-preview","google/gemini-2.5-flash-preview:thinking","openai/o4-mini-high","openai/o3","openai/o4-mini","openai/gpt-4.1","openai/gpt-4.1-mini","openai/gpt-4.1-nano","x-ai/grok-3-beta","meta-llama/llama-4-maverick","meta-llama/llama-4-scout","all-hands/openhands-lm-32b-v0.1","mistral/ministral-8b","google/gemini-2.5-pro-exp-03-25:free","deepseek/deepseek-chat-v3-0324","mistralai/mistral-small-3.1-24b-instruct:free","mistralai/mistral-small-3.1-24b-instruct","ai21/jamba-1.6-large","ai21/jamba-1.6-mini","qwen/qwq-32b","openai/gpt-4.5-preview","google/gemini-2.0-flash-lite-001","anthropic/claude-3.7-sonnet","anthropic/claude-3.7-sonnet:thinking","anthropic/claude-3.7-sonnet:beta","mistralai/mistral-saba","openai/o3-mini-high","google/gemini-2.0-flash-001","qwen/qwen-turbo","qwen/qwen-plus","qwen/qwen-max","openai/o3-mini","mistralai/mistral-small-24b-instruct-2501","deepseek/deepseek-r1-distill-llama-70b","deepseek/deepseek-r1","mistralai/codestral-2501","deepseek/deepseek-chat","openai/o1","x-ai/grok-2-1212","google/gemini-2.0-flash-exp:free","meta-llama/llama-3.3-70b-instruct","amazon/nova-lite-v1","amazon/nova-micro-v1","amazon/nova-pro-v1","openai/gpt-4o-2024-11-20","mistralai/mistral-large-2411","mistralai/mistral-large-2407","mistralai/pixtral-large-2411","anthropic/claude-3.5-haiku:beta","anthropic/claude-3.5-haiku","anthropic/claude-3.5-haiku-20241022:beta","anthropic/claude-3.5-haiku-20241022","anthropic/claude-3.5-sonnet:beta","anthropic/claude-3.5-sonnet","x-ai/grok-beta","mistralai/ministral-8b","mistralai/ministral-3b","nvidia/llama-3.1-nemotron-70b-instruct","google/gemini-flash-1.5-8b","meta-llama/llama-3.2-3b-instruct","qwen/qwen-2.5-72b-instruct","mistralai/pixtral-12b","cohere/command-r-plus-08-2024","cohere/command-r-08-2024","google/gemini-flash-1.5-8b-exp","ai21/jamba-1-5-mini","ai21/jamba-1-5-large","microsoft/phi-3.5-mini-128k-instruct","nousresearch/hermes-3-llama-3.1-70b","openai/gpt-4o-2024-08-06","meta-llama/llama-3.1-8b-instruct","meta-llama/llama-3.1-405b-instruct","meta-llama/llama-3.1-70b-instruct","mistralai/codestral-mamba","mistralai/mistral-nemo","openai/gpt-4o-mini","openai/gpt-4o-mini-2024-07-18","anthropic/claude-3.5-sonnet-20240620:beta","anthropic/claude-3.5-sonnet-20240620","mistralai/mistral-7b-instruct:free","mistralai/mistral-7b-instruct","mistralai/mistral-7b-instruct-v0.3","microsoft/phi-3-mini-128k-instruct","microsoft/phi-3-medium-128k-instruct","google/gemini-flash-1.5","openai/gpt-4o","openai/gpt-4o:extended","openai/gpt-4o-2024-05-13","meta-llama/llama-3-8b-instruct","meta-llama/llama-3-70b-instruct","mistralai/mixtral-8x22b-instruct","google/gemini-pro-1.5","openai/gpt-4-turbo","cohere/command-r-plus","cohere/command-r-plus-04-2024","cohere/command-r","anthropic/claude-3-haiku:beta","anthropic/claude-3-haiku","anthropic/claude-3-opus:beta","anthropic/claude-3-opus","anthropic/claude-3-sonnet:beta","anthropic/claude-3-sonnet","cohere/command-r-03-2024","mistralai/mistral-large","openai/gpt-3.5-turbo-0613","openai/gpt-4-turbo-preview","mistralai/mistral-medium","mistralai/mistral-small","mistralai/mistral-tiny","mistralai/mixtral-8x7b-instruct","openai/gpt-3.5-turbo-1106","openai/gpt-4-1106-preview","mistralai/mistral-7b-instruct-v0.1","openai/gpt-3.5-turbo-16k","openai/gpt-4-32k","openai/gpt-4-32k-0314","openai/gpt-3.5-turbo","openai/gpt-3.5-turbo-0125","openai/gpt-4","openai/gpt-4-0314"].includes(a)}
|
package/dist/api/prompt.js
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { logWrite } from "../log/log.js";
|
|
3
|
-
export async function api_prompt(c) {
|
|
4
|
-
const body = await c.req.json();
|
|
5
|
-
logWrite({ type: 'prompt', args: body });
|
|
6
|
-
const question = body.prompt;
|
|
7
|
-
if (!question) {
|
|
8
|
-
return c.json({ error: 'Prompt is required' }, 400);
|
|
9
|
-
}
|
|
10
|
-
const answer = await prompt(question);
|
|
11
|
-
return c.json({ answer });
|
|
12
|
-
}
|
|
1
|
+
import{prompt}from"../prompt.js";import{logWrite}from"../log/log.js";export async function api_prompt(r){const o=await r.req.json();logWrite({type:"prompt",args:o});const t=o.prompt;if(!t)return r.json({error:"Prompt is required"},400);const p=await prompt(t);return r.json({answer:p})}
|
package/dist/api/settings.js
CHANGED
|
@@ -1,41 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { getJSON, setJSON } from "../utils/json-file.js";
|
|
3
|
-
import { httpValidationErrror as httpValidationError, isEmpty } from "./utils.js";
|
|
4
|
-
const defaultOpenRouterURL = "https://openrouter.ai/api/v1";
|
|
5
|
-
export const defaultModel = "meta-llama/llama-3.3-70b-instruct";
|
|
6
|
-
export async function api_get_settings(c) {
|
|
7
|
-
return c.json(getSettings());
|
|
8
|
-
}
|
|
9
|
-
export async function api_save_settings(c) {
|
|
10
|
-
const settings = await c.req.json();
|
|
11
|
-
if (settings.openRouterApiKey == undefined) {
|
|
12
|
-
httpValidationError("openRouterApiKey is required");
|
|
13
|
-
}
|
|
14
|
-
if (isEmpty(settings.openRouterURL)) {
|
|
15
|
-
settings.openRouterURL = defaultOpenRouterURL;
|
|
16
|
-
}
|
|
17
|
-
if (isEmpty(settings.defaultModel) || settings.defaultModel == 'Select a Model') {
|
|
18
|
-
settings.defaultModel = defaultModel;
|
|
19
|
-
}
|
|
20
|
-
if (settings.openRouterURL == undefined) {
|
|
21
|
-
httpValidationError("openRouterURL is required");
|
|
22
|
-
}
|
|
23
|
-
if (!settings.openRouterURL.startsWith("https://")) {
|
|
24
|
-
httpValidationError("openRouterURL must start with https://");
|
|
25
|
-
}
|
|
26
|
-
// Remove trailing slash if present
|
|
27
|
-
if (settings.openRouterURL.endsWith("/")) {
|
|
28
|
-
settings.openRouterURL = settings.openRouterURL.slice(0, -1);
|
|
29
|
-
}
|
|
30
|
-
setJSON('settings', settings);
|
|
31
|
-
return c.json(getSettings());
|
|
32
|
-
}
|
|
33
|
-
export function getSettings() {
|
|
34
|
-
const defaultSettings = {
|
|
35
|
-
openRouterApiKey: "",
|
|
36
|
-
openRouterURL: defaultOpenRouterURL,
|
|
37
|
-
defaultModel: defaultModel
|
|
38
|
-
};
|
|
39
|
-
const settings = getJSON("settings", defaultSettings);
|
|
40
|
-
return settings;
|
|
41
|
-
}
|
|
1
|
+
import{}from"../interfaces/settings.js";import{getJSON,setJSON}from"../utils/json-file.js";import{httpValidationErrror as httpValidationError,isEmpty}from"./utils.js";const defaultOpenRouterURL="https://openrouter.ai/api/v1";export const defaultModel="meta-llama/llama-3.3-70b-instruct";export async function api_get_settings(t){return t.json(getSettings())}export async function api_save_settings(t){const e=await t.req.json();return null==e.openRouterApiKey&&httpValidationError("openRouterApiKey is required"),isEmpty(e.openRouterURL)&&(e.openRouterURL=defaultOpenRouterURL),(isEmpty(e.defaultModel)||"Select a Model"==e.defaultModel)&&(e.defaultModel=defaultModel),null==e.openRouterURL&&httpValidationError("openRouterURL is required"),e.openRouterURL.startsWith("https://")||httpValidationError("openRouterURL must start with https://"),e.openRouterURL.endsWith("/")&&(e.openRouterURL=e.openRouterURL.slice(0,-1)),setJSON("settings",e),t.json(getSettings())}export function getSettings(){return getJSON("settings",{openRouterApiKey:"",openRouterURL:defaultOpenRouterURL,defaultModel:defaultModel})}
|
package/dist/api/test.js
CHANGED
|
@@ -1,30 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { stripMarkdown } from "../utils/strip-markdown.js";
|
|
3
|
-
export async function createTest(definition, codeFilename, code) {
|
|
4
|
-
const functions = definition.map((tool) => tool.function.name);
|
|
5
|
-
let test = `import { ${functions.join(',')} } from "./${codeFilename}";\n\n`;
|
|
6
|
-
const comments = functions.map((functionName) => {
|
|
7
|
-
return ` // Call ${functionName} to test it`;
|
|
8
|
-
});
|
|
9
|
-
test += `export async function test() {
|
|
10
|
-
${comments.join('\n')}
|
|
11
|
-
return 'place your test results here';
|
|
12
|
-
}`;
|
|
13
|
-
// Try to fill out more with AI
|
|
14
|
-
try {
|
|
15
|
-
const updated = await prompt(`Complete the test function with a test case for the following functions ${functions.join(', ')}. The test should be a valid JavaScript code and should return the test results.
|
|
16
|
-
Here is the initial test code:\n
|
|
17
|
-
${test}
|
|
18
|
-
`, {
|
|
19
|
-
ignoreTools: true, firstMessage: {
|
|
20
|
-
role: "system",
|
|
21
|
-
content: "You must return the code without any explaination."
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
test = stripMarkdown(updated);
|
|
25
|
-
}
|
|
26
|
-
catch (e) {
|
|
27
|
-
console.error("Error generating test code:", e);
|
|
28
|
-
}
|
|
29
|
-
return test;
|
|
30
|
-
}
|
|
1
|
+
import{prompt}from"../prompt.js";import{stripMarkdown}from"../utils/strip-markdown.js";export async function createTest(t,e,o){const n=t.map((t=>t.function.name));let r=`import { ${n.join(",")} } from "./${e}";\n\n`;r+=`export async function test() {\n ${n.map((t=>` // Call ${t} to test it`)).join("\n")}\n return 'place your test results here';\n}`;try{const t=await prompt(`Complete the test function with a test case for the following functions ${n.join(", ")}. The test should be a valid JavaScript code and should return the test results.\n Here is the initial test code:\n\n ${r}\n `,{ignoreTools:!0,firstMessage:{role:"system",content:"You must return the code without any explaination."}});r=stripMarkdown(t)}catch(t){console.error("Error generating test code:",t)}return r}
|
package/dist/api/tools.js
CHANGED
|
@@ -1,313 +1 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, promises,
|
|
2
|
-
import { extname, join } from "path";
|
|
3
|
-
import { extensionLess, isEmpty, tarskFolder } from "./utils.js";
|
|
4
|
-
import { logError, logWrite } from "../log/log.js";
|
|
5
|
-
import tsBlankSpace from "ts-blank-space";
|
|
6
|
-
import { loadTools } from "../tools.js";
|
|
7
|
-
import { callFunction } from "../agent/agent.js";
|
|
8
|
-
import { readAsJSONIfExists } from "../utils/files.js";
|
|
9
|
-
import ts, { SyntaxKind } from "typescript";
|
|
10
|
-
import { createTest } from "./test.js";
|
|
11
|
-
export async function api_run_tool(c) {
|
|
12
|
-
const tool = await c.req.json();
|
|
13
|
-
logWrite({ type: "api_run_tool", args: { toolName: tool.name } });
|
|
14
|
-
if (!tool.code) {
|
|
15
|
-
return c.json({ error: "Tool not found" }, 404);
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
const toolToRun = await getTool(tool.name);
|
|
19
|
-
const tools = await loadTools(toolJavascriptFilenames(true));
|
|
20
|
-
const d = tools.find((t) => t.name == toolToRun.name + '.test');
|
|
21
|
-
const functionName = 'test';
|
|
22
|
-
if (!d) {
|
|
23
|
-
throw new Error(`${tool.name} not found`);
|
|
24
|
-
}
|
|
25
|
-
if (!d.functionNames.includes(functionName)) {
|
|
26
|
-
throw new Error(`${tool.name} function "${functionName}" not found (reload?)`);
|
|
27
|
-
}
|
|
28
|
-
const output = await callFunction(d.functionMap[functionName]);
|
|
29
|
-
// Run the code here
|
|
30
|
-
return c.json({ output, js: 'test();' });
|
|
31
|
-
}
|
|
32
|
-
catch (e) {
|
|
33
|
-
return c.json({ output: `${e}`, js: '' });
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// POST /tools to save a tool's typescript definition, convert it to javascript and validate it
|
|
37
|
-
export async function api_save_tool(c) {
|
|
38
|
-
const tool = await c.req.json();
|
|
39
|
-
if (tool.name == "") {
|
|
40
|
-
tool.name = tool.title;
|
|
41
|
-
}
|
|
42
|
-
const title = tool.title;
|
|
43
|
-
const code = tool.code;
|
|
44
|
-
let test = tool.test ?? "";
|
|
45
|
-
const name = tool.name.toLowerCase().replaceAll(" ", "_");
|
|
46
|
-
if (!title || !code || !name) {
|
|
47
|
-
return c.json({ error: "Title and code are required" }, 400);
|
|
48
|
-
}
|
|
49
|
-
const meta = { title, name };
|
|
50
|
-
// Convert typescript to javascript
|
|
51
|
-
// let answer = await prompt(
|
|
52
|
-
// `Given the following typescript return the equivalent javascript. Do not explain just return the javascript\n${tool.code}`
|
|
53
|
-
// );
|
|
54
|
-
// Get rid of the markdown code block AI adds
|
|
55
|
-
// answer = stripMarkdown(answer);
|
|
56
|
-
const jsCode = tsBlankSpace(tool.code ?? "", (e) => {
|
|
57
|
-
logError({ type: "failed_ts_to_js_code", args: e });
|
|
58
|
-
});
|
|
59
|
-
const testCode = tsBlankSpace(tool.test ?? "", (e) => {
|
|
60
|
-
logError({ type: "failed_ts_to_js_test", args: e });
|
|
61
|
-
});
|
|
62
|
-
// Choose a filename
|
|
63
|
-
const filename = name.replace(/\s+/g, "_").toLowerCase();
|
|
64
|
-
const srcPath = join(toolsSrcFolder(), `${filename}.ts`);
|
|
65
|
-
const jsCodePath = join(toolsJSFolder(), `${filename}.js`);
|
|
66
|
-
const defPath = join(toolsJSFolder(), `${filename}.js.definition.json`);
|
|
67
|
-
const testPath = join(toolsSrcFolder(), `${filename}.test.ts`);
|
|
68
|
-
const jsTestPath = join(toolsJSFolder(), `${filename}.test.js`);
|
|
69
|
-
const metaPath = join(toolsSrcFolder(), `${filename}.json`);
|
|
70
|
-
const currentMeta = readAsJSONIfExists(metaPath);
|
|
71
|
-
if (currentMeta) {
|
|
72
|
-
meta.revision = (currentMeta.revision ?? 1) + 1;
|
|
73
|
-
}
|
|
74
|
-
const missingData = {
|
|
75
|
-
missingParameters: [],
|
|
76
|
-
missingFunctions: []
|
|
77
|
-
};
|
|
78
|
-
const definition = await inspectCode(srcPath, missingData);
|
|
79
|
-
logWrite({ type: "api_save_tool_check_test", args: { test } });
|
|
80
|
-
if (isEmpty(test)) {
|
|
81
|
-
test = await createTest(definition, `${filename}.js`, code);
|
|
82
|
-
}
|
|
83
|
-
// Save the Typescript
|
|
84
|
-
writeFileSync(srcPath, code, "utf-8");
|
|
85
|
-
// Save the meta data
|
|
86
|
-
writeFileSync(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
87
|
-
// Write the function definition
|
|
88
|
-
writeFileSync(defPath, JSON.stringify(definition, null, 2), "utf-8");
|
|
89
|
-
// Save the Test
|
|
90
|
-
writeFileSync(testPath, test, "utf-8");
|
|
91
|
-
// Save the Javascript Code
|
|
92
|
-
writeFileSync(jsCodePath, jsCode, "utf-8");
|
|
93
|
-
// Save the Javascript Test
|
|
94
|
-
writeFileSync(jsTestPath, testCode, "utf-8");
|
|
95
|
-
logWrite({ type: "api_save_tool", args: { test: testPath, code: srcPath, meta: metaPath } });
|
|
96
|
-
return c.json({
|
|
97
|
-
message: `Tool saved successfully (${jsCodePath})`,
|
|
98
|
-
missingParameters: missingData.missingParameters,
|
|
99
|
-
missingFunctions: missingData.missingFunctions,
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
// Get the tool with code
|
|
103
|
-
export async function api_get_tool(c) {
|
|
104
|
-
const toolName = c.req.param("tool");
|
|
105
|
-
const tool = await getTool(toolName);
|
|
106
|
-
console.log(`api_get_tool ${toolName}`);
|
|
107
|
-
return c.json(tool);
|
|
108
|
-
}
|
|
109
|
-
// Delete the tool with code
|
|
110
|
-
export async function api_delete_tool(c) {
|
|
111
|
-
const toolName = c.req.param("tool");
|
|
112
|
-
await deleteTool(toolName);
|
|
113
|
-
return c.json({});
|
|
114
|
-
}
|
|
115
|
-
async function deleteTool(name) {
|
|
116
|
-
console.log(`Deleting tool ${name}`);
|
|
117
|
-
const toolsFolder = toolsSrcFolder();
|
|
118
|
-
await promises.rm(join(toolsFolder, `${name}.ts`));
|
|
119
|
-
await promises.rm(join(toolsFolder, `${name}.test.ts`));
|
|
120
|
-
await promises.rm(join(toolsFolder, `${name}.json`));
|
|
121
|
-
await promises.rm(join(toolsJSFolder(), `${name}.js`));
|
|
122
|
-
await promises.rm(join(toolsJSFolder(), `${name}.test.js`));
|
|
123
|
-
}
|
|
124
|
-
// Get a list of tools (code is omitted)
|
|
125
|
-
export async function api_get_tools(c) {
|
|
126
|
-
const fileList = await toolFilenames();
|
|
127
|
-
const result = [];
|
|
128
|
-
for (const file of fileList) {
|
|
129
|
-
const tool = await getTool(file);
|
|
130
|
-
tool.code = undefined;
|
|
131
|
-
result.push(tool);
|
|
132
|
-
}
|
|
133
|
-
return c.json(result);
|
|
134
|
-
}
|
|
135
|
-
// Typescript files for tools
|
|
136
|
-
async function toolFilenames() {
|
|
137
|
-
const toolsFolder = toolsSrcFolder();
|
|
138
|
-
const files = await promises.readdir(toolsFolder);
|
|
139
|
-
return files
|
|
140
|
-
.filter((f) => extname(f) === ".ts" && !f.endsWith(".test.ts"))
|
|
141
|
-
.map((f) => extensionLess(f));
|
|
142
|
-
}
|
|
143
|
-
// Returns the compiled tools folder
|
|
144
|
-
function toolsJSFolder() {
|
|
145
|
-
const folderPath = join(tarskFolder(), ".tools");
|
|
146
|
-
if (!existsSync(folderPath)) {
|
|
147
|
-
mkdirSync(folderPath, { recursive: true });
|
|
148
|
-
}
|
|
149
|
-
return folderPath;
|
|
150
|
-
}
|
|
151
|
-
// Javascript files for tools and tests
|
|
152
|
-
export function toolJavascriptFilenames(includeTests) {
|
|
153
|
-
const files = readdirSync(toolsJSFolder());
|
|
154
|
-
if (includeTests) {
|
|
155
|
-
return files
|
|
156
|
-
.filter((f) => extname(f) === ".js")
|
|
157
|
-
.map((f) => join(tarskFolder(), ".tools", f));
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
return files
|
|
161
|
-
.filter((f) => extname(f) === ".js" && !f.endsWith(".test.js"))
|
|
162
|
-
.map((f) => join(tarskFolder(), ".tools", f));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
export async function getTool(name) {
|
|
166
|
-
const toolsFolder = toolsSrcFolder();
|
|
167
|
-
const code = await readOrEmpty(join(toolsFolder, `${name}.ts`));
|
|
168
|
-
const test = await readOrEmpty(join(toolsFolder, `${name}.test.ts`));
|
|
169
|
-
const meta = await readOrEmpty(join(toolsFolder, `${name}.json`));
|
|
170
|
-
try {
|
|
171
|
-
const json = meta == '' ? { tile: '', name: '' } : JSON.parse(meta);
|
|
172
|
-
return { code, title: json.title, name: json.name, test };
|
|
173
|
-
}
|
|
174
|
-
catch (e) {
|
|
175
|
-
console.error(`Failed to parse ${name}.json`, e);
|
|
176
|
-
return { code, title: "", name, test };
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// Returns the source tools folder
|
|
180
|
-
function toolsSrcFolder() {
|
|
181
|
-
const folderPath = join(tarskFolder(), "tools");
|
|
182
|
-
if (!existsSync(folderPath)) {
|
|
183
|
-
mkdirSync(folderPath, { recursive: true });
|
|
184
|
-
}
|
|
185
|
-
return folderPath;
|
|
186
|
-
}
|
|
187
|
-
async function readOrEmpty(filename) {
|
|
188
|
-
if (!existsSync(filename)) {
|
|
189
|
-
return "";
|
|
190
|
-
}
|
|
191
|
-
return await promises.readFile(filename, "utf-8");
|
|
192
|
-
}
|
|
193
|
-
export async function inspectCode(filename, missingData) {
|
|
194
|
-
const code = await readOrEmpty(filename);
|
|
195
|
-
const tools = [];
|
|
196
|
-
const sourceFile = ts.createSourceFile('temp.ts', code, ts.ScriptTarget.Latest, true);
|
|
197
|
-
function visit(node) {
|
|
198
|
-
if ((ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node)) && node.name) {
|
|
199
|
-
const parsed = parseJSDoc(node);
|
|
200
|
-
console.log("Parsed function", parsed);
|
|
201
|
-
const properties = {};
|
|
202
|
-
const required = [];
|
|
203
|
-
node.parameters.forEach(param => {
|
|
204
|
-
const isOptional = !!param.questionToken || !!param.initializer;
|
|
205
|
-
if (!isOptional) {
|
|
206
|
-
required.push(param.name.getText());
|
|
207
|
-
}
|
|
208
|
-
let type = typeName(param.type?.getText());
|
|
209
|
-
if (param.type?.kind == SyntaxKind.TypeReference) {
|
|
210
|
-
type = 'object';
|
|
211
|
-
}
|
|
212
|
-
parsed.params;
|
|
213
|
-
const hasDescription = Object.keys(parsed.params).includes(param.name.getText());
|
|
214
|
-
const description = hasDescription ? parsed.params[param.name.getText()] : '';
|
|
215
|
-
if (!hasDescription) {
|
|
216
|
-
missingData.missingParameters.push(param.name.escapedText);
|
|
217
|
-
}
|
|
218
|
-
properties[param.name.getText()] = {
|
|
219
|
-
type,
|
|
220
|
-
description
|
|
221
|
-
};
|
|
222
|
-
if (type == 'array') {
|
|
223
|
-
properties[param.name.getText()].items = {
|
|
224
|
-
type: arrayType(param.type?.getText()),
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
const functionDescription = parsed.description;
|
|
229
|
-
if (isEmpty(functionDescription)) {
|
|
230
|
-
missingData.missingFunctions.push(node.name.getText());
|
|
231
|
-
}
|
|
232
|
-
tools.push({
|
|
233
|
-
type: 'function',
|
|
234
|
-
function: {
|
|
235
|
-
name: node.name?.text ?? '',
|
|
236
|
-
description: functionDescription,
|
|
237
|
-
parameters: {
|
|
238
|
-
type: 'object',
|
|
239
|
-
properties,
|
|
240
|
-
required,
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
ts.forEachChild(node, visit);
|
|
246
|
-
}
|
|
247
|
-
visit(sourceFile);
|
|
248
|
-
return tools;
|
|
249
|
-
}
|
|
250
|
-
function parseJSDoc(node) {
|
|
251
|
-
const functionName = node.name?.getText();
|
|
252
|
-
const jsDoc = ts.getJSDocCommentsAndTags(node);
|
|
253
|
-
const parsed = {
|
|
254
|
-
functionName,
|
|
255
|
-
description: "",
|
|
256
|
-
params: {},
|
|
257
|
-
};
|
|
258
|
-
let info = '';
|
|
259
|
-
for (const doc of jsDoc) {
|
|
260
|
-
if (ts.isJSDoc(doc)) {
|
|
261
|
-
if (doc.comment) {
|
|
262
|
-
parsed.description = doc.comment.toString();
|
|
263
|
-
}
|
|
264
|
-
if (doc.tags) {
|
|
265
|
-
for (const tag of doc.tags) {
|
|
266
|
-
info += ' ' + tag.comment;
|
|
267
|
-
// if (ts.isJSDocPropertyTag(tag)) {
|
|
268
|
-
if (tag.kind == SyntaxKind.JSDocTag && tag.comment) {
|
|
269
|
-
let txt = tag.comment ? tag.comment.toString() : "";
|
|
270
|
-
const lines = txt.split(' ');
|
|
271
|
-
const paramName = lines[0];
|
|
272
|
-
if (txt.startsWith(paramName)) {
|
|
273
|
-
txt = txt.substring(paramName.length).trim();
|
|
274
|
-
}
|
|
275
|
-
if (txt.startsWith('-')) {
|
|
276
|
-
txt = txt.substring(1).trim();
|
|
277
|
-
}
|
|
278
|
-
parsed.params[paramName] = txt;
|
|
279
|
-
}
|
|
280
|
-
if (tag.kind == SyntaxKind.JSDocFunctionType ||
|
|
281
|
-
tag.kind == SyntaxKind.JSDocParameterTag) {
|
|
282
|
-
let txt = tag.comment ? tag.comment.toString() : "";
|
|
283
|
-
//const lines = txt.split(' ');
|
|
284
|
-
let paramName = tag.name?.escapedText;
|
|
285
|
-
//const paramName = tag.name.escapedText.toString();// lines[0];
|
|
286
|
-
if (txt.startsWith(paramName)) {
|
|
287
|
-
txt = txt.substring(paramName.length).trim();
|
|
288
|
-
}
|
|
289
|
-
if (txt.startsWith('-')) {
|
|
290
|
-
txt = txt.substring(1).trim();
|
|
291
|
-
}
|
|
292
|
-
//parsed.params[paramName] = paramDesc;
|
|
293
|
-
parsed.params[paramName] = txt;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return parsed;
|
|
300
|
-
}
|
|
301
|
-
function typeName(name) {
|
|
302
|
-
if (!name)
|
|
303
|
-
return 'any';
|
|
304
|
-
if (name.endsWith('[]')) {
|
|
305
|
-
return 'array';
|
|
306
|
-
}
|
|
307
|
-
return name;
|
|
308
|
-
}
|
|
309
|
-
function arrayType(name) {
|
|
310
|
-
if (!name)
|
|
311
|
-
return 'any';
|
|
312
|
-
return name.replace('[]', '');
|
|
313
|
-
}
|
|
1
|
+
import{existsSync,mkdirSync,promises,readdirSync,writeFileSync}from"fs";import{extname,join}from"path";import{extensionLess,isEmpty,tarskFolder}from"./utils.js";import{logError,logWrite}from"../log/log.js";import tsBlankSpace from"ts-blank-space";import{loadTools}from"../tools.js";import{callFunction}from"../agent/agent.js";import{readAsJSONIfExists}from"../utils/files.js";import ts,{SyntaxKind}from"typescript";import{createTest}from"./test.js";export async function api_run_tool(t){const e=await t.req.json();if(logWrite({type:"api_run_tool",args:{toolName:e.name}}),!e.code)return t.json({error:"Tool not found"},404);try{const o=await getTool(e.name),s=(await loadTools(toolJavascriptFilenames(!0))).find((t=>t.name==o.name+".test")),n="test";if(!s)throw new Error(`${e.name} not found`);if(!s.functionNames.includes(n))throw new Error(`${e.name} function "${n}" not found (reload?)`);const r=await callFunction(s.functionMap[n]);return t.json({output:r,js:"test();"})}catch(e){return t.json({output:`${e}`,js:""})}}export async function api_save_tool(t){const e=await t.req.json();""==e.name&&(e.name=e.title);const o=e.title,s=e.code;let n=e.test??"";const r=e.name.toLowerCase().replaceAll(" ","_");if(!o||!s||!r)return t.json({error:"Title and code are required"},400);const i={title:o,name:r},a=tsBlankSpace(e.code??"",(t=>{logError({type:"failed_ts_to_js_code",args:t})})),c=tsBlankSpace(e.test??"",(t=>{logError({type:"failed_ts_to_js_test",args:t})})),l=r.replace(/\s+/g,"_").toLowerCase(),m=join(toolsSrcFolder(),`${l}.ts`),p=join(toolsJSFolder(),`${l}.js`),u=join(toolsJSFolder(),`${l}.js.definition.json`),d=join(toolsSrcFolder(),`${l}.test.ts`),f=join(toolsJSFolder(),`${l}.test.js`),y=join(toolsSrcFolder(),`${l}.json`),g=readAsJSONIfExists(y);g&&(i.revision=(g.revision??1)+1);const j={missingParameters:[],missingFunctions:[]},S=await inspectCode(m,j);return logWrite({type:"api_save_tool_check_test",args:{test:n}}),isEmpty(n)&&(n=await createTest(S,`${l}.js`,s)),writeFileSync(m,s,"utf-8"),writeFileSync(y,JSON.stringify(i,null,2),"utf-8"),writeFileSync(u,JSON.stringify(S,null,2),"utf-8"),writeFileSync(d,n,"utf-8"),writeFileSync(p,a,"utf-8"),writeFileSync(f,c,"utf-8"),logWrite({type:"api_save_tool",args:{test:d,code:m,meta:y}}),t.json({message:`Tool saved successfully (${p})`,missingParameters:j.missingParameters,missingFunctions:j.missingFunctions})}export async function api_get_tool(t){const e=t.req.param("tool"),o=await getTool(e);return console.log(`api_get_tool ${e}`),t.json(o)}export async function api_delete_tool(t){const e=t.req.param("tool");return await deleteTool(e),t.json({})}async function deleteTool(t){console.log(`Deleting tool ${t}`);const e=toolsSrcFolder();await promises.rm(join(e,`${t}.ts`)),await promises.rm(join(e,`${t}.test.ts`)),await promises.rm(join(e,`${t}.json`)),await promises.rm(join(toolsJSFolder(),`${t}.js`)),await promises.rm(join(toolsJSFolder(),`${t}.test.js`))}export async function api_get_tools(t){const e=await toolFilenames(),o=[];for(const t of e){const e=await getTool(t);e.code=void 0,o.push(e)}return t.json(o)}async function toolFilenames(){const t=toolsSrcFolder();return(await promises.readdir(t)).filter((t=>".ts"===extname(t)&&!t.endsWith(".test.ts"))).map((t=>extensionLess(t)))}function toolsJSFolder(){const t=join(tarskFolder(),".tools");return existsSync(t)||mkdirSync(t,{recursive:!0}),t}export function toolJavascriptFilenames(t){const e=readdirSync(toolsJSFolder());return t?e.filter((t=>".js"===extname(t))).map((t=>join(tarskFolder(),".tools",t))):e.filter((t=>".js"===extname(t)&&!t.endsWith(".test.js"))).map((t=>join(tarskFolder(),".tools",t)))}export async function getTool(t){const e=toolsSrcFolder(),o=await readOrEmpty(join(e,`${t}.ts`)),s=await readOrEmpty(join(e,`${t}.test.ts`)),n=await readOrEmpty(join(e,`${t}.json`));try{const t=""==n?{tile:"",name:""}:JSON.parse(n);return{code:o,title:t.title,name:t.name,test:s}}catch(e){return console.error(`Failed to parse ${t}.json`,e),{code:o,title:"",name:t,test:s}}}function toolsSrcFolder(){const t=join(tarskFolder(),"tools");return existsSync(t)||mkdirSync(t,{recursive:!0}),t}async function readOrEmpty(t){return existsSync(t)?await promises.readFile(t,"utf-8"):""}export async function inspectCode(t,e){const o=await readOrEmpty(t),s=[];return function t(o){if((ts.isFunctionDeclaration(o)||ts.isFunctionExpression(o)||ts.isArrowFunction(o))&&o.name){const t=parseJSDoc(o);console.log("Parsed function",t);const n={},r=[];o.parameters.forEach((o=>{!!o.questionToken||!!o.initializer||r.push(o.name.getText());let s=typeName(o.type?.getText());o.type?.kind==SyntaxKind.TypeReference&&(s="object"),t.params;const i=Object.keys(t.params).includes(o.name.getText()),a=i?t.params[o.name.getText()]:"";i||e.missingParameters.push(o.name.escapedText),n[o.name.getText()]={type:s,description:a},"array"==s&&(n[o.name.getText()].items={type:arrayType(o.type?.getText())})}));const i=t.description;isEmpty(i)&&e.missingFunctions.push(o.name.getText()),s.push({type:"function",function:{name:o.name?.text??"",description:i,parameters:{type:"object",properties:n,required:r}}})}ts.forEachChild(o,t)}(ts.createSourceFile("temp.ts",o,ts.ScriptTarget.Latest,!0)),s}function parseJSDoc(t){const e=t.name?.getText(),o=ts.getJSDocCommentsAndTags(t),s={functionName:e,description:"",params:{}};let n="";for(const t of o)if(ts.isJSDoc(t)&&(t.comment&&(s.description=t.comment.toString()),t.tags))for(const e of t.tags){if(n+=" "+e.comment,e.kind==SyntaxKind.JSDocTag&&e.comment){let t=e.comment?e.comment.toString():"";const o=t.split(" ")[0];t.startsWith(o)&&(t=t.substring(o.length).trim()),t.startsWith("-")&&(t=t.substring(1).trim()),s.params[o]=t}if(e.kind==SyntaxKind.JSDocFunctionType||e.kind==SyntaxKind.JSDocParameterTag){let t=e.comment?e.comment.toString():"",o=e.name?.escapedText;t.startsWith(o)&&(t=t.substring(o.length).trim()),t.startsWith("-")&&(t=t.substring(1).trim()),s.params[o]=t}}return s}function typeName(t){return t?t.endsWith("[]")?"array":t:"any"}function arrayType(t){return t?t.replace("[]",""):"any"}
|
package/dist/api/utils.js
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { homedir } from "os";
|
|
3
|
-
import { extname, join } from "path";
|
|
4
|
-
export function extensionLess(filename) {
|
|
5
|
-
return filename.substring(0, filename.length - extname(filename).length);
|
|
6
|
-
}
|
|
7
|
-
export function tarskFolder() {
|
|
8
|
-
return join(homedir(), ".tarsk");
|
|
9
|
-
}
|
|
10
|
-
export function httpValidationErrror(message) {
|
|
11
|
-
throw new HTTPException(406, { message });
|
|
12
|
-
}
|
|
13
|
-
export function isEmpty(v) {
|
|
14
|
-
if (v == undefined || v == null || v == "" || v.trim() == "") {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
1
|
+
import{HTTPException}from"hono/http-exception";import{homedir}from"os";import{extname,join}from"path";export function extensionLess(t){return t.substring(0,t.length-extname(t).length)}export function tarskFolder(){return join(homedir(),".tarsk")}export function httpValidationErrror(t){throw new HTTPException(406,{message:t})}export function isEmpty(t){return null==t||null==t||""==t||""==t.trim()}
|
package/dist/index.js
CHANGED
|
@@ -1,41 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import { Hono } from "hono";
|
|
4
|
-
import { cors } from "hono/cors";
|
|
5
|
-
import open from 'open';
|
|
6
|
-
import { api_prompt } from "./api/prompt.js";
|
|
7
|
-
import { api_delete_tool, api_get_tool, api_get_tools, api_run_tool, api_save_tool, } from "./api/tools.js";
|
|
8
|
-
import { api_get_settings, api_save_settings } from "./api/settings.js";
|
|
9
|
-
import { api_get_models } from "./api/models.js";
|
|
10
|
-
const app = new Hono();
|
|
11
|
-
app.use("/*", cors({
|
|
12
|
-
origin: (origin, c) => {
|
|
13
|
-
return (origin.startsWith("http://localhost") ||
|
|
14
|
-
origin.startsWith('https://tarsk.io')) ? origin : "x";
|
|
15
|
-
},
|
|
16
|
-
}));
|
|
17
|
-
// API endpoints
|
|
18
|
-
app.get("/", (c) => {
|
|
19
|
-
return c.text("Tarsk Started.");
|
|
20
|
-
});
|
|
21
|
-
app.post("/prompt", async (c) => await api_prompt(c));
|
|
22
|
-
app.post("/tools", async (c) => await api_save_tool(c));
|
|
23
|
-
app.get("/tools", async (c) => await api_get_tools(c));
|
|
24
|
-
app.post("/run/tool", async (c) => await api_run_tool(c));
|
|
25
|
-
app.get("/tools/:tool", async (c) => await api_get_tool(c));
|
|
26
|
-
app.delete("/tools/:tool", async (c) => await api_delete_tool(c));
|
|
27
|
-
app.get("/settings", async (c) => await api_get_settings(c));
|
|
28
|
-
app.post("/settings", async (c) => await api_save_settings(c));
|
|
29
|
-
app.get("/models", async (c) => await api_get_models(c));
|
|
30
|
-
serve({
|
|
31
|
-
fetch: app.fetch,
|
|
32
|
-
port: 4021,
|
|
33
|
-
}, (info) => {
|
|
34
|
-
if (process.env._ && process.env._.endsWith('tsx')) {
|
|
35
|
-
console.log(`Tarsk is running in dev mode on http://localhost:${info.port}`);
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
console.log(`Tarsk is running. Visit https://tarsk.io`); //
|
|
39
|
-
open("https://tarsk.io");
|
|
40
|
-
}
|
|
41
|
-
});
|
|
2
|
+
import{serve}from"@hono/node-server";import{Hono}from"hono";import{cors}from"hono/cors";import open from"open";import{api_prompt}from"./api/prompt.js";import{api_delete_tool,api_get_tool,api_get_tools,api_run_tool,api_save_tool}from"./api/tools.js";import{api_get_settings,api_save_settings}from"./api/settings.js";import{api_get_models}from"./api/models.js";const app=new Hono;app.use("/*",cors({origin:(t,o)=>t.startsWith("http://localhost")||t.startsWith("https://tarsk.io")?t:"x"})),app.get("/",(t=>t.text("Tarsk Started."))),app.post("/prompt",(async t=>await api_prompt(t))),app.post("/tools",(async t=>await api_save_tool(t))),app.get("/tools",(async t=>await api_get_tools(t))),app.post("/run/tool",(async t=>await api_run_tool(t))),app.get("/tools/:tool",(async t=>await api_get_tool(t))),app.delete("/tools/:tool",(async t=>await api_delete_tool(t))),app.get("/settings",(async t=>await api_get_settings(t))),app.post("/settings",(async t=>await api_save_settings(t))),app.get("/models",(async t=>await api_get_models(t))),serve({fetch:app.fetch,port:4021},(t=>{process.env._&&process.env._.endsWith("tsx")?console.log(`Tarsk is running in dev mode on http://localhost:${t.port}`):(console.log("Tarsk is running. Visit https://tarsk.io"),open("https://tarsk.io"))}));
|
package/dist/interfaces/meta.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{};
|
package/dist/interfaces/model.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{};
|
package/dist/log/log.js
CHANGED
|
@@ -1,35 +1 @@
|
|
|
1
|
-
let logLevel = "
|
|
2
|
-
// Structured logging
|
|
3
|
-
export function logWrite(l, additionalMessage) {
|
|
4
|
-
if (logLevel == "none")
|
|
5
|
-
return;
|
|
6
|
-
let msg = l.type;
|
|
7
|
-
if (l.message) {
|
|
8
|
-
msg += `: ${l.message}`;
|
|
9
|
-
}
|
|
10
|
-
if (l.args) {
|
|
11
|
-
msg += ` ${JSON.stringify(l.args)}`;
|
|
12
|
-
}
|
|
13
|
-
l.id = id();
|
|
14
|
-
console.log(msg);
|
|
15
|
-
// console.log(JSON.stringify(l));
|
|
16
|
-
}
|
|
17
|
-
export function logStart(l, additionalMessage) {
|
|
18
|
-
if (logLevel === "none")
|
|
19
|
-
return;
|
|
20
|
-
console.time(l.type);
|
|
21
|
-
logWrite(l, additionalMessage);
|
|
22
|
-
}
|
|
23
|
-
export function logEnd(label) {
|
|
24
|
-
if (logLevel === "none")
|
|
25
|
-
return;
|
|
26
|
-
console.timeEnd(label);
|
|
27
|
-
}
|
|
28
|
-
export function logError(l) {
|
|
29
|
-
if (logLevel === "none")
|
|
30
|
-
return;
|
|
31
|
-
console.error(l);
|
|
32
|
-
}
|
|
33
|
-
function id() {
|
|
34
|
-
return Math.random().toString().replace(".", "");
|
|
35
|
-
}
|
|
1
|
+
let logLevel="debug";export function logWrite(e,o){if("none"==logLevel)return;let n=e.type;e.message&&(n+=`: ${e.message}`),e.args&&(n+=` ${JSON.stringify(e.args)}`),e.id=id(),console.log(n)}export function logStart(e,o){"none"!==logLevel&&(console.time(e.type),logWrite(e,o))}export function logEnd(e){"none"!==logLevel&&console.timeEnd(e)}export function logError(e){"none"!==logLevel&&console.error(e)}function id(){return Math.random().toString().replace(".","")}
|
package/dist/prompt.js
CHANGED
|
@@ -1,52 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { loadTools } from "./tools.js";
|
|
3
|
-
import { defaultModel, getSettings } from "./api/settings.js";
|
|
4
|
-
import { toolJavascriptFilenames } from "./api/tools.js";
|
|
5
|
-
import { isEmpty } from "./api/utils.js";
|
|
6
|
-
import { logWrite, logEnd, logStart } from "./log/log.js";
|
|
7
|
-
export async function prompt(content, promptOptions) {
|
|
8
|
-
const settings = await getSettings();
|
|
9
|
-
if (settings.openRouterApiKey == "") {
|
|
10
|
-
return "You need to set the OpenRouter API key in the settings.";
|
|
11
|
-
}
|
|
12
|
-
const options = {
|
|
13
|
-
baseUrl: settings.openRouterURL,
|
|
14
|
-
apiKey: settings.openRouterApiKey,
|
|
15
|
-
};
|
|
16
|
-
const messages = [];
|
|
17
|
-
if (promptOptions?.firstMessage) {
|
|
18
|
-
messages.push(promptOptions.firstMessage);
|
|
19
|
-
}
|
|
20
|
-
messages.push({ role: "user", content });
|
|
21
|
-
const model = isEmpty(settings.defaultModel)
|
|
22
|
-
? defaultModel
|
|
23
|
-
: settings.defaultModel;
|
|
24
|
-
// Load the tools needed for the request
|
|
25
|
-
logWrite({ type: "prompt", args: content }, `The model is ${model}`);
|
|
26
|
-
const filenames = toolJavascriptFilenames(false);
|
|
27
|
-
let tools = [];
|
|
28
|
-
if (!promptOptions?.ignoreTools) {
|
|
29
|
-
logStart({ type: "load_tools", args: filenames });
|
|
30
|
-
tools = await loadTools(filenames);
|
|
31
|
-
logEnd("load_tools");
|
|
32
|
-
}
|
|
33
|
-
const request = { model, tools, messages };
|
|
34
|
-
try {
|
|
35
|
-
const response = await completion(request, options);
|
|
36
|
-
logWrite({ type: "completion_response", args: response });
|
|
37
|
-
logWrite({ type: "message", args: response?.choices[0].message });
|
|
38
|
-
return response?.choices[0].message.content;
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
return friendlyError(`${e}`, settings);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function friendlyError(e, settings) {
|
|
45
|
-
if (e.startsWith("Error: No endpoints found that support tool use")) {
|
|
46
|
-
return `The model "${settings.defaultModel}" does not support tool use. Please select a different model (A good default is ${defaultModel}).`;
|
|
47
|
-
}
|
|
48
|
-
if (e.startsWith("Error: No auth credentials found")) {
|
|
49
|
-
return `The OpenRouter API key is invalid. Please check your account at https://openrouter.ai and get a new key.`;
|
|
50
|
-
}
|
|
51
|
-
return e;
|
|
52
|
-
}
|
|
1
|
+
import{completion}from"./agent/agent.js";import{loadTools}from"./tools.js";import{defaultModel,getSettings}from"./api/settings.js";import{toolJavascriptFilenames}from"./api/tools.js";import{isEmpty}from"./api/utils.js";import{logWrite,logEnd,logStart}from"./log/log.js";export async function prompt(e,o){const t=await getSettings();if(""==t.openRouterApiKey)return"You need to set the OpenRouter API key in the settings.";const s={baseUrl:t.openRouterURL,apiKey:t.openRouterApiKey},r=[];o?.firstMessage&&r.push(o.firstMessage),r.push({role:"user",content:e});const l=isEmpty(t.defaultModel)?defaultModel:t.defaultModel;logWrite({type:"prompt",args:e},`The model is ${l}`);const a=toolJavascriptFilenames(!1);let i=[];o?.ignoreTools||(logStart({type:"load_tools",args:a}),i=await loadTools(a),logEnd("load_tools"));const n={model:l,tools:i,messages:r};try{const e=await completion(n,s);return logWrite({type:"completion_response",args:e}),logWrite({type:"message",args:e?.choices[0].message}),e?.choices[0].message.content}catch(e){return friendlyError(`${e}`,t)}}function friendlyError(e,o){return e.startsWith("Error: No endpoints found that support tool use")?`The model "${o.defaultModel}" does not support tool use. Please select a different model (A good default is ${defaultModel}).`:e.startsWith("Error: No auth credentials found")?"The OpenRouter API key is invalid. Please check your account at https://openrouter.ai and get a new key.":e}
|
package/dist/tools/books.js
CHANGED
|
@@ -1,39 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// @param @array @required searchTerms List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)
|
|
3
|
-
export async function searchGutenbergBooks(searchTerms) {
|
|
4
|
-
const searchQuery = searchTerms.join(' ');
|
|
5
|
-
const url = 'https://gutendex.com/books';
|
|
6
|
-
const response = await fetch(`${url}?search=${searchQuery}`);
|
|
7
|
-
const data = await response.json();
|
|
8
|
-
return data.results.map((book) => ({
|
|
9
|
-
id: book.id,
|
|
10
|
-
title: book.title,
|
|
11
|
-
authors: book.authors,
|
|
12
|
-
}));
|
|
13
|
-
}
|
|
14
|
-
export default function tools() {
|
|
15
|
-
return booksTools;
|
|
16
|
-
}
|
|
17
|
-
// Automate via TS Morph?
|
|
18
|
-
const booksTools = [
|
|
19
|
-
{
|
|
20
|
-
type: 'function',
|
|
21
|
-
function: {
|
|
22
|
-
name: 'searchGutenbergBooks',
|
|
23
|
-
description: 'Search for books in the Project Gutenberg library based on specified search terms',
|
|
24
|
-
parameters: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
properties: {
|
|
27
|
-
searchTerms: {
|
|
28
|
-
type: 'array',
|
|
29
|
-
items: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
},
|
|
32
|
-
description: "List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
required: ['searchTerms'],
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
];
|
|
1
|
+
export async function searchGutenbergBooks(e){const t=e.join(" "),o=await fetch(`https://gutendex.com/books?search=${t}`);return(await o.json()).results.map((e=>({id:e.id,title:e.title,authors:e.authors})))}export default function tools(){return booksTools}const booksTools=[{type:"function",function:{name:"searchGutenbergBooks",description:"Search for books in the Project Gutenberg library based on specified search terms",parameters:{type:"object",properties:{searchTerms:{type:"array",items:{type:"string"},description:"List of search terms to find books in the Gutenberg library (e.g. ['dickens', 'great'] to search for books by Dickens with 'great' in the title)"}},required:["searchTerms"]}}}];
|
package/dist/tools.js
CHANGED
|
@@ -1,91 +1 @@
|
|
|
1
|
-
import { logWrite,
|
|
2
|
-
import { getPathWithoutExtension, readAsJSONIfExists } from "./utils/files.js";
|
|
3
|
-
export async function loadTools(modulePaths) {
|
|
4
|
-
const tools = [];
|
|
5
|
-
logWrite({
|
|
6
|
-
type: "load_tools",
|
|
7
|
-
args: modulePaths,
|
|
8
|
-
}, `modulePaths is "${modulePaths.join(", ")}"`);
|
|
9
|
-
for (const modulePath of modulePaths) {
|
|
10
|
-
logWrite({ type: "getTool", args: modulePath });
|
|
11
|
-
if (modulePath == "") {
|
|
12
|
-
logError({ type: "getTool", args: "Empty modulePath" });
|
|
13
|
-
continue;
|
|
14
|
-
}
|
|
15
|
-
else {
|
|
16
|
-
const tool = await inspectTool(modulePath);
|
|
17
|
-
tools.push(tool);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return tools;
|
|
21
|
-
}
|
|
22
|
-
function findMetaData(modulePath) {
|
|
23
|
-
// Load the meta data: its in the toolsfolder not the .tools folder
|
|
24
|
-
let metaPath = (getPathWithoutExtension(modulePath) + ".json").replace('.tools', 'tools');
|
|
25
|
-
const meta = readAsJSONIfExists(metaPath);
|
|
26
|
-
if (meta)
|
|
27
|
-
return meta;
|
|
28
|
-
// This could also be a test module and the tools meta data is in the file without .test
|
|
29
|
-
metaPath = metaPath.replace('.test', '');
|
|
30
|
-
const meta2 = readAsJSONIfExists(metaPath);
|
|
31
|
-
if (meta2)
|
|
32
|
-
meta2.name = meta2.name + '.test';
|
|
33
|
-
if (meta2)
|
|
34
|
-
return meta2;
|
|
35
|
-
}
|
|
36
|
-
// eg modulePath = './tools/books.js'
|
|
37
|
-
async function inspectTool(modulePath) {
|
|
38
|
-
const result = {
|
|
39
|
-
functionNames: [],
|
|
40
|
-
functionMap: undefined,
|
|
41
|
-
definition: undefined,
|
|
42
|
-
name: undefined,
|
|
43
|
-
path: modulePath,
|
|
44
|
-
revision: undefined
|
|
45
|
-
};
|
|
46
|
-
logStart({ type: "load_tool", args: { modulePath } }, `The modulePath is "${modulePath}"`);
|
|
47
|
-
try {
|
|
48
|
-
const meta = findMetaData(modulePath);
|
|
49
|
-
result.revision = meta?.revision;
|
|
50
|
-
result.name = meta?.name;
|
|
51
|
-
const isTest = modulePath.endsWith(".test.js");
|
|
52
|
-
// This ensure that tools are loaded fresh if they are changed (based on revision) and
|
|
53
|
-
// test are always loaded fresh
|
|
54
|
-
const query = isTest ? `?${Math.random()}` : `?${result.revision}`;
|
|
55
|
-
const myModule = await import(modulePath + query);
|
|
56
|
-
try {
|
|
57
|
-
result.functionNames = Object.keys(myModule);
|
|
58
|
-
result.functionMap = {};
|
|
59
|
-
// All exported function are
|
|
60
|
-
for (const func of result.functionNames) {
|
|
61
|
-
logWrite({ type: "load_tool_function", args: { foundFunction: func } });
|
|
62
|
-
result.functionMap[func] = myModule[func];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
throw new Error(`Error loading Map from ${modulePath}: ${e}`);
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
// This is the definition needed for the tool for the agent (TODO: Generate this)
|
|
70
|
-
if (!isTest) {
|
|
71
|
-
result.definition = getDefinition(modulePath);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch (e) {
|
|
75
|
-
throw new Error(`Error loading the agent map from ${modulePath}: ${e}`);
|
|
76
|
-
}
|
|
77
|
-
return result;
|
|
78
|
-
}
|
|
79
|
-
catch (e) {
|
|
80
|
-
logError({ type: "load_tool_error", args: e });
|
|
81
|
-
throw new Error(`load_tool_error ${modulePath}: ${e}`);
|
|
82
|
-
}
|
|
83
|
-
finally {
|
|
84
|
-
logEnd("load_tool");
|
|
85
|
-
logWrite({ type: 'load_tool', args: { result } });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function getDefinition(modulePath) {
|
|
89
|
-
const filename = modulePath + '.definition.json';
|
|
90
|
-
return readAsJSONIfExists(filename);
|
|
91
|
-
}
|
|
1
|
+
import{logWrite,logEnd,logError,logStart}from"./log/log.js";import{getPathWithoutExtension,readAsJSONIfExists}from"./utils/files.js";export async function loadTools(o){const t=[];logWrite({type:"load_tools",args:o},`modulePaths is "${o.join(", ")}"`);for(const n of o)if(logWrite({type:"getTool",args:n}),""!=n){const o=await inspectTool(n);t.push(o)}else logError({type:"getTool",args:"Empty modulePath"});return t}function findMetaData(o){let t=(getPathWithoutExtension(o)+".json").replace(".tools","tools");const n=readAsJSONIfExists(t);if(n)return n;t=t.replace(".test","");const r=readAsJSONIfExists(t);return r&&(r.name=r.name+".test"),r||void 0}async function inspectTool(o){const t={functionNames:[],functionMap:void 0,definition:void 0,name:void 0,path:o,revision:void 0};logStart({type:"load_tool",args:{modulePath:o}},`The modulePath is "${o}"`);try{const n=findMetaData(o);t.revision=n?.revision,t.name=n?.name;const r=o.endsWith(".test.js"),e=r?`?${Math.random()}`:`?${t.revision}`,i=await import(o+e);try{t.functionNames=Object.keys(i),t.functionMap={};for(const o of t.functionNames)logWrite({type:"load_tool_function",args:{foundFunction:o}}),t.functionMap[o]=i[o]}catch(t){throw new Error(`Error loading Map from ${o}: ${t}`)}try{r||(t.definition=getDefinition(o))}catch(t){throw new Error(`Error loading the agent map from ${o}: ${t}`)}return t}catch(t){throw logError({type:"load_tool_error",args:t}),new Error(`load_tool_error ${o}: ${t}`)}finally{logEnd("load_tool"),logWrite({type:"load_tool",args:{result:t}})}}function getDefinition(o){return readAsJSONIfExists(o+".definition.json")}
|
package/dist/utils/files.js
CHANGED
|
@@ -1,14 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function getPathWithoutExtension(path) {
|
|
3
|
-
const lastDotIndex = path.lastIndexOf(".");
|
|
4
|
-
if (lastDotIndex === -1) {
|
|
5
|
-
return path; // No extension found, return the original path
|
|
6
|
-
}
|
|
7
|
-
return path.slice(0, lastDotIndex);
|
|
8
|
-
}
|
|
9
|
-
export function readAsJSONIfExists(path) {
|
|
10
|
-
if (existsSync(path)) {
|
|
11
|
-
const data = readFileSync(path, "utf-8");
|
|
12
|
-
return JSON.parse(data);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
1
|
+
import{existsSync,readFileSync}from"fs";export function getPathWithoutExtension(t){const e=t.lastIndexOf(".");return-1===e?t:t.slice(0,e)}export function readAsJSONIfExists(t){if(existsSync(t)){const e=readFileSync(t,"utf-8");return JSON.parse(e)}}
|
package/dist/utils/json-file.js
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
import { join }
|
|
2
|
-
import { tarskFolder } from "../api/utils.js";
|
|
3
|
-
import { existsSync, mkdir, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
-
import { decryptString, encryptString } from "../api/encryption.js";
|
|
5
|
-
export function getJSON(name, defaultValue) {
|
|
6
|
-
const filename = join(tarskFolder(), name + '.json');
|
|
7
|
-
if (!existsSync(filename)) {
|
|
8
|
-
if (!existsSync(tarskFolder())) {
|
|
9
|
-
mkdirSync(tarskFolder(), { recursive: true });
|
|
10
|
-
}
|
|
11
|
-
setJSON(name, defaultValue);
|
|
12
|
-
return defaultValue;
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
try {
|
|
16
|
-
const data = readFileSync(filename, 'utf-8');
|
|
17
|
-
return JSON.parse(decryptString(data));
|
|
18
|
-
}
|
|
19
|
-
catch (e) {
|
|
20
|
-
setJSON(name, defaultValue);
|
|
21
|
-
//console.error(`Error reading ${name}.json: ${e}`);
|
|
22
|
-
return defaultValue;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
export function setJSON(name, value) {
|
|
27
|
-
const filename = join(tarskFolder(), name + '.json');
|
|
28
|
-
writeFileSync(filename, encryptString(JSON.stringify(value)));
|
|
29
|
-
}
|
|
1
|
+
import{join}from"path";import{tarskFolder}from"../api/utils.js";import{existsSync,mkdir,mkdirSync,readFileSync,writeFileSync}from"fs";import{decryptString,encryptString}from"../api/encryption.js";export function getJSON(r,t){const e=join(tarskFolder(),r+".json");if(!existsSync(e))return existsSync(tarskFolder())||mkdirSync(tarskFolder(),{recursive:!0}),setJSON(r,t),t;try{const r=readFileSync(e,"utf-8");return JSON.parse(decryptString(r))}catch(e){return setJSON(r,t),t}}export function setJSON(r,t){const e=join(tarskFolder(),r+".json");writeFileSync(e,encryptString(JSON.stringify(t)))}
|
|
@@ -1,7 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export function stripMarkdown(markdown) {
|
|
3
|
-
// Remove the code block lines
|
|
4
|
-
const lines = markdown.split("\n");
|
|
5
|
-
const strippedLines = lines.filter(line => !line.startsWith("```"));
|
|
6
|
-
return strippedLines.join("\n").trim();
|
|
7
|
-
}
|
|
1
|
+
export function stripMarkdown(t){return t.split("\n").filter((t=>!t.startsWith("```"))).join("\n").trim()}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tarsk",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"author": "WebNative LLC",
|
|
5
5
|
"description": "Tarsk is a AI tool available at https://tarsk.io",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"build:prod": "tsc && rm -rf dist/test && node scripts/minify.js",
|
|
11
|
-
"publish": "npm run build:prod && npm publish",
|
|
12
11
|
"start": "npm run build && npm link && tarsk",
|
|
13
12
|
"restart": "npm unlink tarsk && rm -rf dist && npm run build && npm link && tarsk",
|
|
14
13
|
"test": "vitest",
|