nothumanallowed 16.0.50 → 16.0.51
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/package.json +1 -1
- package/src/commands/ask.mjs +2 -2
- package/src/constants.mjs +1 -1
- package/src/server/routes/webcraft.mjs +4 -2
- package/src/services/llm.mjs +54 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nothumanallowed",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.51",
|
|
4
4
|
"description": "Local AI assistant: 80 tools (Gmail, Calendar, Drive, GitHub, Slack, browser, code, files), 38 agents, visual workflows (Studio, AWF, WebCraft). Install with `npm i -g nothumanallowed`, run with `nha ui`. Free tier built-in (Liara), no API key required. Your data stays on your PC — OAuth tokens local, no cloud. Open-source MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/commands/ask.mjs
CHANGED
|
@@ -182,11 +182,11 @@ export async function cmdAsk(args) {
|
|
|
182
182
|
const callFn = getProviderCall(provider);
|
|
183
183
|
if (!callFn) {
|
|
184
184
|
fail(`Unknown provider: ${provider}`);
|
|
185
|
-
info('Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere');
|
|
185
|
+
info('Supported: anthropic, openai, gemini, deepseek, grok, mistral, cohere, openrouter');
|
|
186
186
|
process.exit(1);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
-
const useStream = stream && (provider === 'anthropic' || provider === 'openai' || provider === 'deepseek' || provider === 'grok' || provider === 'mistral');
|
|
189
|
+
const useStream = stream && (provider === 'anthropic' || provider === 'openai' || provider === 'deepseek' || provider === 'grok' || provider === 'mistral' || provider === 'openrouter');
|
|
190
190
|
const result = await callFn(apiKey, model, systemPrompt, userMessage, useStream);
|
|
191
191
|
|
|
192
192
|
if (!useStream && result) {
|
package/src/constants.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
|
|
|
5
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
6
6
|
const __dirname = path.dirname(__filename);
|
|
7
7
|
|
|
8
|
-
export const VERSION = '16.0.
|
|
8
|
+
export const VERSION = '16.0.51';
|
|
9
9
|
export const BASE_URL = 'https://nothumanallowed.com/cli';
|
|
10
10
|
export const API_BASE = 'https://nothumanallowed.com/api/v1';
|
|
11
11
|
|
|
@@ -1373,9 +1373,11 @@ RULES:
|
|
|
1373
1373
|
return `Error: unknown tool ${toolName}`;
|
|
1374
1374
|
}
|
|
1375
1375
|
|
|
1376
|
-
// Try native tool calling first
|
|
1376
|
+
// Try native tool calling first. Providers that support OpenAI-style native
|
|
1377
|
+
// tool_use: Anthropic, OpenAI, and OpenRouter (which proxies to Claude/GPT/etc.
|
|
1378
|
+
// with the same OpenAI-compatible schema).
|
|
1377
1379
|
const provider = config.llm?.provider || 'anthropic';
|
|
1378
|
-
const useNativeTools = provider === 'anthropic' || provider === 'openai';
|
|
1380
|
+
const useNativeTools = provider === 'anthropic' || provider === 'openai' || provider === 'openrouter';
|
|
1379
1381
|
|
|
1380
1382
|
if (useNativeTools) {
|
|
1381
1383
|
const systemPrompt = buildSystemPrompt();
|
package/src/services/llm.mjs
CHANGED
|
@@ -563,6 +563,44 @@ export async function callGrok(apiKey, model, systemPrompt, userMessage, stream
|
|
|
563
563
|
return data.choices?.[0]?.message?.content || '';
|
|
564
564
|
}
|
|
565
565
|
|
|
566
|
+
/**
|
|
567
|
+
* OpenRouter — aggregator that exposes 100+ models (Claude, GPT, Gemini,
|
|
568
|
+
* Mistral, Llama, Qwen, DeepSeek, etc.) via a single OpenAI-compatible API.
|
|
569
|
+
* Endpoint: https://openrouter.ai/api/v1/chat/completions
|
|
570
|
+
* Model names: "anthropic/claude-sonnet-4.5", "openai/gpt-4o", "google/gemini-2.5-pro"...
|
|
571
|
+
*/
|
|
572
|
+
export async function callOpenRouter(apiKey, model, systemPrompt, userMessage, stream = false, opts = {}) {
|
|
573
|
+
const body = {
|
|
574
|
+
model: model || 'anthropic/claude-sonnet-4.5',
|
|
575
|
+
max_tokens: opts.max_tokens || 8192,
|
|
576
|
+
messages: [
|
|
577
|
+
{ role: 'system', content: systemPrompt },
|
|
578
|
+
..._openaiHistory(opts),
|
|
579
|
+
{ role: 'user', content: userMessage },
|
|
580
|
+
],
|
|
581
|
+
stream,
|
|
582
|
+
};
|
|
583
|
+
if (opts.temperature !== undefined) body.temperature = opts.temperature;
|
|
584
|
+
const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
|
|
585
|
+
method: 'POST',
|
|
586
|
+
headers: {
|
|
587
|
+
'Content-Type': 'application/json',
|
|
588
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
589
|
+
// OpenRouter recommends these for proper attribution + ranking
|
|
590
|
+
'HTTP-Referer': 'https://nothumanallowed.com',
|
|
591
|
+
'X-Title': 'NotHumanAllowed CLI',
|
|
592
|
+
},
|
|
593
|
+
body: JSON.stringify(body),
|
|
594
|
+
});
|
|
595
|
+
if (!res.ok) {
|
|
596
|
+
const err = await res.text();
|
|
597
|
+
throw new Error(`OpenRouter ${res.status}: ${err}`);
|
|
598
|
+
}
|
|
599
|
+
if (stream) return streamSSE(res, 'openai');
|
|
600
|
+
const data = await res.json();
|
|
601
|
+
return data.choices?.[0]?.message?.content || '';
|
|
602
|
+
}
|
|
603
|
+
|
|
566
604
|
export async function callMistral(apiKey, model, systemPrompt, userMessage, stream = false, opts = {}) {
|
|
567
605
|
const body = {
|
|
568
606
|
model: model || 'mistral-large-latest',
|
|
@@ -753,6 +791,7 @@ const PROVIDERS = {
|
|
|
753
791
|
grok: callGrok,
|
|
754
792
|
mistral: callMistral,
|
|
755
793
|
cohere: callCohere,
|
|
794
|
+
openrouter: callOpenRouter,
|
|
756
795
|
};
|
|
757
796
|
|
|
758
797
|
export function getProviderCall(provider) {
|
|
@@ -788,6 +827,12 @@ export async function callLLMWithTools(config, systemPrompt, messages, tools, on
|
|
|
788
827
|
return _callOpenAIWithTools(apiKey, model, systemPrompt, messages, tools, onText, onToolCall, opts);
|
|
789
828
|
}
|
|
790
829
|
|
|
830
|
+
// OpenRouter — uses OpenAI-compatible function calling schema. We reuse the
|
|
831
|
+
// OpenAI tool-call implementation but point to OpenRouter's endpoint.
|
|
832
|
+
if (provider === 'openrouter') {
|
|
833
|
+
return _callOpenAIWithTools(apiKey, model || 'anthropic/claude-sonnet-4.5', systemPrompt, messages, tools, onText, onToolCall, { ...opts, baseUrl: 'https://openrouter.ai/api/v1', referer: 'https://nothumanallowed.com', xTitle: 'NotHumanAllowed CLI' });
|
|
834
|
+
}
|
|
835
|
+
|
|
791
836
|
// Other providers: fallback to text-based tool calling (old system)
|
|
792
837
|
// The caller should handle this by checking the return value
|
|
793
838
|
return null;
|
|
@@ -972,12 +1017,17 @@ async function _callOpenAIWithTools(apiKey, model, systemPrompt, messages, tools
|
|
|
972
1017
|
stream: true,
|
|
973
1018
|
};
|
|
974
1019
|
|
|
975
|
-
|
|
1020
|
+
// Support baseUrl override so OpenRouter (OpenAI-compatible) can reuse this loop.
|
|
1021
|
+
const endpoint = (opts.baseUrl || 'https://api.openai.com/v1') + '/chat/completions';
|
|
1022
|
+
const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}` };
|
|
1023
|
+
if (opts.referer) headers['HTTP-Referer'] = opts.referer;
|
|
1024
|
+
if (opts.xTitle) headers['X-Title'] = opts.xTitle;
|
|
1025
|
+
const res = await fetch(endpoint, {
|
|
976
1026
|
method: 'POST',
|
|
977
|
-
headers
|
|
1027
|
+
headers,
|
|
978
1028
|
body: JSON.stringify(body),
|
|
979
1029
|
});
|
|
980
|
-
if (!res.ok) { const err = await res.text(); throw new Error(
|
|
1030
|
+
if (!res.ok) { const err = await res.text(); throw new Error(`${opts.baseUrl ? 'OpenRouter' : 'OpenAI'} ${res.status}: ${err}`); }
|
|
981
1031
|
|
|
982
1032
|
const reader = res.body.getReader();
|
|
983
1033
|
const dec = new TextDecoder();
|
|
@@ -1053,6 +1103,7 @@ export function getApiKey(config, provider) {
|
|
|
1053
1103
|
grok: config.llm.grokKey || config.llm.apiKey,
|
|
1054
1104
|
mistral: config.llm.mistralKey || config.llm.apiKey,
|
|
1055
1105
|
cohere: config.llm.cohereKey || config.llm.apiKey,
|
|
1106
|
+
openrouter: config.llm.openrouterKey || config.llm.openrouter_key || config.llm.apiKey,
|
|
1056
1107
|
};
|
|
1057
1108
|
return keyMap[provider] || config.llm.apiKey;
|
|
1058
1109
|
}
|