Merge pull request #430 from kolbytn/model_params

Model Params + Updated Gemini
This commit is contained in:
Max Robinson 2025-02-04 15:07:56 -06:00 committed by GitHub
commit 116b7ac83e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 156 additions and 135 deletions

View file

@ -41,7 +41,7 @@ You can configure the agent's name, model, and prompts in their profile like `an
| OpenAI | `OPENAI_API_KEY` | `gpt-4o-mini` | [docs](https://platform.openai.com/docs/models) | | OpenAI | `OPENAI_API_KEY` | `gpt-4o-mini` | [docs](https://platform.openai.com/docs/models) |
| Google | `GEMINI_API_KEY` | `gemini-pro` | [docs](https://ai.google.dev/gemini-api/docs/models/gemini) | | Google | `GEMINI_API_KEY` | `gemini-pro` | [docs](https://ai.google.dev/gemini-api/docs/models/gemini) |
| Anthropic | `ANTHROPIC_API_KEY` | `claude-3-haiku-20240307` | [docs](https://docs.anthropic.com/claude/docs/models-overview) | | Anthropic | `ANTHROPIC_API_KEY` | `claude-3-haiku-20240307` | [docs](https://docs.anthropic.com/claude/docs/models-overview) |
| Replicate | `REPLICATE_API_KEY` | `meta/meta-llama-3-70b-instruct` | [docs](https://replicate.com/collections/language-models) | | Replicate | `REPLICATE_API_KEY` | `replicate/meta/meta-llama-3-70b-instruct` | [docs](https://replicate.com/collections/language-models) |
| Ollama (local) | n/a | `llama3` | [docs](https://ollama.com/library) | | Ollama (local) | n/a | `llama3` | [docs](https://ollama.com/library) |
| Groq | `GROQCLOUD_API_KEY` | `groq/mixtral-8x7b-32768` | [docs](https://console.groq.com/docs/models) | | Groq | `GROQCLOUD_API_KEY` | `groq/mixtral-8x7b-32768` | [docs](https://console.groq.com/docs/models) |
| Hugging Face | `HUGGINGFACE_API_KEY` | `huggingface/mistralai/Mistral-Nemo-Instruct-2407` | [docs](https://huggingface.co/models) | | Hugging Face | `HUGGINGFACE_API_KEY` | `huggingface/mistralai/Mistral-Nemo-Instruct-2407` | [docs](https://huggingface.co/models) |
@ -105,40 +105,37 @@ node main.js --profiles ./profiles/andy.json ./profiles/jill.json
### Model Specifications ### Model Specifications
LLM backends can be specified as simply as `"model": "gpt-3.5-turbo"`. However, for both the chat model and the embedding model, the bot profile can specify the below attributes: LLM models can be specified as simply as `"model": "gpt-4o"`. However, you can specify different models for chat, coding, and embeddings.
You can pass a string or an object for these fields. A model object must specify an `api`, and optionally a `model`, `url`, and additional `params`.
```json ```json
"model": { "model": {
"api": "openai", "api": "openai",
"model": "gpt-4o",
"url": "https://api.openai.com/v1/", "url": "https://api.openai.com/v1/",
"model": "gpt-3.5-turbo" "params": {
"max_tokens": 1000,
"temperature": 1
}
},
"code_model": {
"api": "openai",
"model": "gpt-4",
"url": "https://api.openai.com/v1/"
}, },
"embedding": { "embedding": {
"api": "openai", "api": "openai",
"url": "https://api.openai.com/v1/", "url": "https://api.openai.com/v1/",
"model": "text-embedding-ada-002" "model": "text-embedding-ada-002"
} }
``` ```
The model or code_model parameter accepts either a string or object. If a string, it should specify the model to be used. The api and url will be assumed. If an object, the api field must be specified. Each api has a default model and url, so those fields are optional. `model` is used for chat, `code_model` is used for newAction coding, and `embedding` is used to embed text for example selection. If `code_model` is not specified, then it will use `model` for coding.
If the embedding field is not specified, then it will use the default embedding method for the chat model's api (Note that anthropic has no embedding model). The embedding parameter can also be a string or object. If a string, it should specify the embedding api and the default model and url will be used. If a valid embedding is not specified and cannot be assumed, then word overlap will be used to retrieve examples instead. All apis have default models and urls, so those fields are optional. Note some apis have no embedding model, so they will default to word overlap to retrieve examples.
Thus, all the below specifications are equivalent to the above example: The `params` field is optional and can be used to specify additional parameters for the model. It accepts any key-value pairs supported by the api. Is not supported for embedding models.
```json
"model": "gpt-3.5-turbo"
```
```json
"model": {
"api": "openai"
}
```
```json
"model": "gpt-3.5-turbo",
"embedding": "openai"
"code_model": "gpt-3.5-turbo"
```
## Patches ## Patches

View file

@ -1,35 +0,0 @@
diff --git a/node_modules/mineflayer-collectblock/lib/CollectBlock.js b/node_modules/mineflayer-collectblock/lib/CollectBlock.js
index 2c11e8c..bb49c11 100644
--- a/node_modules/mineflayer-collectblock/lib/CollectBlock.js
+++ b/node_modules/mineflayer-collectblock/lib/CollectBlock.js
@@ -77,10 +77,11 @@ function mineBlock(bot, block, options) {
}
yield bot.tool.equipForBlock(block, equipToolOptions);
// @ts-expect-error
- if (!block.canHarvest(bot.heldItem)) {
+ if (bot.heldItem !== null && !block.canHarvest(bot.heldItem.type)) {
options.targets.removeTarget(block);
return;
}
+
const tempEvents = new TemporarySubscriber_1.TemporarySubscriber(bot);
tempEvents.subscribeTo('itemDrop', (entity) => {
if (entity.position.distanceTo(block.position.offset(0.5, 0.5, 0.5)) <= 0.5) {
@@ -92,7 +93,7 @@ function mineBlock(bot, block, options) {
// Waiting for items to drop
yield new Promise(resolve => {
let remainingTicks = 10;
- tempEvents.subscribeTo('physicTick', () => {
+ tempEvents.subscribeTo('physicsTick', () => {
remainingTicks--;
if (remainingTicks <= 0) {
tempEvents.cleanup();
@@ -195,6 +196,8 @@ class CollectBlock {
throw (0, Util_1.error)('UnresolvedDependency', 'The mineflayer-collectblock plugin relies on the mineflayer-tool plugin to run!');
}
if (this.movements != null) {
+ this.movements.dontMineUnderFallingBlock = false;
+ this.movements.dontCreateFlow = false;
this.bot.pathfinder.setMovements(this.movements);
}
if (!optionsFull.append)

View file

@ -1,5 +1,10 @@
{ {
"name": "gpt", "name": "gpt",
"model": "gpt-4o" "model": {
"model": "gpt-4o",
"params": {
"temperature": 0.5
}
}
} }

View file

@ -1,6 +1,6 @@
import { History } from './history.js'; import { History } from './history.js';
import { Coder } from './coder.js'; import { Coder } from './coder.js';
import { Prompter } from './prompter.js'; import { Prompter } from '../models/prompter.js';
import { initModes } from './modes.js'; import { initModes } from './modes.js';
import { initBot } from '../utils/mcdata.js'; import { initBot } from '../utils/mcdata.js';
import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction, blacklistCommands } from './commands/index.js'; import { containsCommand, commandExists, executeCommand, truncCommandMessage, isAction, blacklistCommands } from './commands/index.js';
@ -100,11 +100,9 @@ export class Agent {
}); });
} catch (error) { } catch (error) {
// Ensure we're not losing error details // Ensure we're not losing error details
console.error('Agent start failed with error:', { console.error('Agent start failed with error')
message: error.message || 'No error message', console.error(error)
stack: error.stack || 'No stack trace',
error: error
});
throw error; // Re-throw with preserved details throw error; // Re-throw with preserved details
} }
} }

View file

@ -3,8 +3,9 @@ import { strictFormat } from '../utils/text.js';
import { getKey } from '../utils/keys.js'; import { getKey } from '../utils/keys.js';
export class Claude { export class Claude {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.params = params;
let config = {}; let config = {};
if (url) if (url)
@ -20,13 +21,16 @@ export class Claude {
let res = null; let res = null;
try { try {
console.log('Awaiting anthropic api response...') console.log('Awaiting anthropic api response...')
// console.log('Messages:', messages); if (!this.params.max_tokens) {
this.params.max_tokens = 4096;
}
const resp = await this.anthropic.messages.create({ const resp = await this.anthropic.messages.create({
model: this.model_name || "claude-3-sonnet-20240229", model: this.model_name || "claude-3-sonnet-20240229",
system: systemMessage, system: systemMessage,
max_tokens: 2048,
messages: messages, messages: messages,
...(this.params || {})
}); });
console.log('Received.') console.log('Received.')
res = resp.content[0].text; res = resp.content[0].text;
} }

View file

@ -3,8 +3,9 @@ import { getKey, hasKey } from '../utils/keys.js';
import { strictFormat } from '../utils/text.js'; import { strictFormat } from '../utils/text.js';
export class DeepSeek { export class DeepSeek {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.params = params;
let config = {}; let config = {};
@ -23,6 +24,7 @@ export class DeepSeek {
model: this.model_name || "deepseek-chat", model: this.model_name || "deepseek-chat",
messages, messages,
stop: stop_seq, stop: stop_seq,
...(this.params || {})
}; };
let res = null; let res = null;

View file

@ -1,10 +1,11 @@
import { GoogleGenerativeAI } from '@google/generative-ai'; import { GoogleGenerativeAI } from '@google/generative-ai';
import { toSinglePrompt } from '../utils/text.js'; import { toSinglePrompt, strictFormat } from '../utils/text.js';
import { getKey } from '../utils/keys.js'; import { getKey } from '../utils/keys.js';
export class Gemini { export class Gemini {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.params = params;
this.url = url; this.url = url;
this.safetySettings = [ this.safetySettings = [
{ {
@ -34,28 +35,46 @@ export class Gemini {
async sendRequest(turns, systemMessage) { async sendRequest(turns, systemMessage) {
let model; let model;
const modelConfig = {
model: this.model_name || "gemini-1.5-flash",
// systemInstruction does not work bc google is trash
};
if (this.url) { if (this.url) {
model = this.genAI.getGenerativeModel( model = this.genAI.getGenerativeModel(
{ model: this.model_name || "gemini-1.5-flash" }, modelConfig,
{ baseUrl: this.url }, { baseUrl: this.url },
{ safetySettings: this.safetySettings } { safetySettings: this.safetySettings }
); );
} else { } else {
model = this.genAI.getGenerativeModel( model = this.genAI.getGenerativeModel(
{ model: this.model_name || "gemini-1.5-flash" }, modelConfig,
{ safetySettings: this.safetySettings } { safetySettings: this.safetySettings }
); );
} }
const stop_seq = '***';
const prompt = toSinglePrompt(turns, systemMessage, stop_seq, 'model');
console.log('Awaiting Google API response...'); console.log('Awaiting Google API response...');
const result = await model.generateContent(prompt);
turns.unshift({ role: 'system', content: systemMessage });
turns = strictFormat(turns);
let contents = [];
for (let turn of turns) {
contents.push({
role: turn.role === 'assistant' ? 'model' : 'user',
parts: [{ text: turn.content }]
});
}
const result = await model.generateContent({
contents,
generationConfig: {
...(this.params || {})
}
});
const response = await result.response; const response = await result.response;
const text = response.text(); const text = response.text();
console.log('Received.'); console.log('Received.');
if (!text.includes(stop_seq)) return text;
const idx = text.indexOf(stop_seq);
return text.slice(0, idx); return text.slice(0, idx);
} }

View file

@ -3,8 +3,9 @@ import { getKey, hasKey } from '../utils/keys.js';
import { strictFormat } from '../utils/text.js'; import { strictFormat } from '../utils/text.js';
export class GPT { export class GPT {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.params = params;
let config = {}; let config = {};
if (url) if (url)
@ -25,6 +26,7 @@ export class GPT {
model: this.model_name || "gpt-3.5-turbo", model: this.model_name || "gpt-3.5-turbo",
messages, messages,
stop: stop_seq, stop: stop_seq,
...(this.params || {})
}; };
if (this.model_name.includes('o1')) { if (this.model_name.includes('o1')) {
pack.messages = strictFormat(messages); pack.messages = strictFormat(messages);
@ -32,6 +34,7 @@ export class GPT {
} }
let res = null; let res = null;
try { try {
console.log('Awaiting openai api response from model', this.model_name) console.log('Awaiting openai api response from model', this.model_name)
// console.log('Messages:', messages); // console.log('Messages:', messages);

View file

@ -3,8 +3,10 @@ import { getKey } from '../utils/keys.js';
// xAI doesn't supply a SDK for their models, but fully supports OpenAI and Anthropic SDKs // xAI doesn't supply a SDK for their models, but fully supports OpenAI and Anthropic SDKs
export class Grok { export class Grok {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.url = url;
this.params = params;
let config = {}; let config = {};
if (url) if (url)
@ -23,7 +25,8 @@ export class Grok {
const pack = { const pack = {
model: this.model_name || "grok-beta", model: this.model_name || "grok-beta",
messages, messages,
stop: [stop_seq] stop: [stop_seq],
...(this.params || {})
}; };
let res = null; let res = null;

View file

@ -4,12 +4,13 @@ import { getKey } from '../utils/keys.js';
// Umbrella class for Mixtral, LLama, Gemma... // Umbrella class for Mixtral, LLama, Gemma...
export class GroqCloudAPI { export class GroqCloudAPI {
constructor(model_name, url, max_tokens=16384) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.url = url; this.url = url;
this.max_tokens = max_tokens; this.params = params;
// ReplicateAPI theft :3 // ReplicateAPI theft :3
if (this.url) { if (this.url) {
console.warn("Groq Cloud has no implementation for custom URLs. Ignoring provided URL."); console.warn("Groq Cloud has no implementation for custom URLs. Ignoring provided URL.");
} }
this.groq = new Groq({ apiKey: getKey('GROQCLOUD_API_KEY') }); this.groq = new Groq({ apiKey: getKey('GROQCLOUD_API_KEY') });
@ -20,14 +21,15 @@ export class GroqCloudAPI {
let res = null; let res = null;
try { try {
console.log("Awaiting Groq response..."); console.log("Awaiting Groq response...");
if (!this.params.max_tokens) {
this.params.max_tokens = 16384;
}
let completion = await this.groq.chat.completions.create({ let completion = await this.groq.chat.completions.create({
"messages": messages, "messages": messages,
"model": this.model_name || "mixtral-8x7b-32768", "model": this.model_name || "mixtral-8x7b-32768",
"temperature": 0.2,
"max_tokens": this.max_tokens, // maximum token limit, differs from model to model
"top_p": 1,
"stream": true, "stream": true,
"stop": stop_seq // "***" "stop": stop_seq,
...(this.params || {})
}); });
let temp_res = ""; let temp_res = "";

View file

@ -3,9 +3,10 @@ import {getKey} from '../utils/keys.js';
import {HfInference} from "@huggingface/inference"; import {HfInference} from "@huggingface/inference";
export class HuggingFace { export class HuggingFace {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name.replace('huggingface/',''); this.model_name = model_name.replace('huggingface/','');
this.url = url; this.url = url;
this.params = params;
if (this.url) { if (this.url) {
console.warn("Hugging Face doesn't support custom urls!"); console.warn("Hugging Face doesn't support custom urls!");
@ -25,7 +26,8 @@ export class HuggingFace {
console.log('Awaiting Hugging Face API response...'); console.log('Awaiting Hugging Face API response...');
for await (const chunk of this.huggingface.chatCompletionStream({ for await (const chunk of this.huggingface.chatCompletionStream({
model: model_name, model: model_name,
messages: [{ role: "user", content: input }] messages: [{ role: "user", content: input }],
...(this.params || {})
})) { })) {
res += (chunk.choices[0]?.delta?.content || ""); res += (chunk.choices[0]?.delta?.content || "");
} }

View file

@ -1,8 +1,9 @@
import { strictFormat } from '../utils/text.js'; import { strictFormat } from '../utils/text.js';
export class Local { export class Local {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.params = params;
this.url = url || 'http://127.0.0.1:11434'; this.url = url || 'http://127.0.0.1:11434';
this.chat_endpoint = '/api/chat'; this.chat_endpoint = '/api/chat';
this.embedding_endpoint = '/api/embeddings'; this.embedding_endpoint = '/api/embeddings';
@ -15,7 +16,12 @@ export class Local {
let res = null; let res = null;
try { try {
console.log(`Awaiting local response... (model: ${model})`) console.log(`Awaiting local response... (model: ${model})`)
res = await this.send(this.chat_endpoint, {model: model, messages: messages, stream: false}); res = await this.send(this.chat_endpoint, {
model: model,
messages: messages,
stream: false,
...(this.params || {})
});
if (res) if (res)
res = res['message']['content']; res = res['message']['content'];
} }

View file

@ -5,10 +5,13 @@ import { strictFormat } from '../utils/text.js';
export class Mistral { export class Mistral {
#client; #client;
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name;
this.params = params;
if (typeof url === "string") { if (typeof url === "string") {
console.warn("Mistral does not support custom URL's, ignoring!"); console.warn("Mistral does not support custom URL's, ignoring!");
} }
if (!getKey("MISTRAL_API_KEY")) { if (!getKey("MISTRAL_API_KEY")) {
@ -22,8 +25,6 @@ export class Mistral {
); );
this.model_name = model_name;
// Prevents the following code from running when model not specified // Prevents the following code from running when model not specified
if (typeof this.model_name === "undefined") return; if (typeof this.model_name === "undefined") return;
@ -49,6 +50,7 @@ export class Mistral {
const response = await this.#client.chat.complete({ const response = await this.#client.chat.complete({
model, model,
messages, messages,
...(this.params || {})
}); });
result = response.choices[0].message.content; result = response.choices[0].message.content;

View file

@ -4,9 +4,11 @@ import { strictFormat } from '../utils/text.js';
// llama, mistral // llama, mistral
export class Novita { export class Novita {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name.replace('novita/', ''); this.model_name = model_name.replace('novita/', '');
this.url = url || 'https://api.novita.ai/v3/openai'; this.url = url || 'https://api.novita.ai/v3/openai';
this.params = params;
let config = { let config = {
baseURL: this.url baseURL: this.url
@ -26,6 +28,7 @@ export class Novita {
model: this.model_name || "meta-llama/llama-3.1-70b-instruct", model: this.model_name || "meta-llama/llama-3.1-70b-instruct",
messages, messages,
stop: [stop_seq], stop: [stop_seq],
...(this.params || {})
}; };
let res = null; let res = null;

View file

@ -1,23 +1,23 @@
import { readFileSync, mkdirSync, writeFileSync} from 'fs'; import { readFileSync, mkdirSync, writeFileSync} from 'fs';
import { Examples } from '../utils/examples.js'; import { Examples } from '../utils/examples.js';
import { getCommandDocs } from './commands/index.js'; import { getCommandDocs } from '../agent/commands/index.js';
import { getSkillDocs } from './library/index.js'; import { getSkillDocs } from '../agent/library/index.js';
import { stringifyTurns } from '../utils/text.js'; import { stringifyTurns } from '../utils/text.js';
import { getCommand } from './commands/index.js'; import { getCommand } from '../agent/commands/index.js';
import settings from '../../settings.js'; import settings from '../../settings.js';
import { Gemini } from '../models/gemini.js'; import { Gemini } from './gemini.js';
import { GPT } from '../models/gpt.js'; import { GPT } from './gpt.js';
import { Claude } from '../models/claude.js'; import { Claude } from './claude.js';
import { Mistral } from '../models/mistral.js'; import { Mistral } from './mistral.js';
import { ReplicateAPI } from '../models/replicate.js'; import { ReplicateAPI } from './replicate.js';
import { Local } from '../models/local.js'; import { Local } from './local.js';
import { Novita } from '../models/novita.js'; import { Novita } from './novita.js';
import { GroqCloudAPI } from '../models/groq.js'; import { GroqCloudAPI } from './groq.js';
import { HuggingFace } from '../models/huggingface.js'; import { HuggingFace } from './huggingface.js';
import { Qwen } from "../models/qwen.js"; import { Qwen } from "./qwen.js";
import { Grok } from "../models/grok.js"; import { Grok } from "./grok.js";
import { DeepSeek } from '../models/deepseek.js'; import { DeepSeek } from './deepseek.js';
export class Prompter { export class Prompter {
constructor(agent, fp) { constructor(agent, fp) {
@ -102,6 +102,8 @@ export class Prompter {
_selectAPI(profile) { _selectAPI(profile) {
if (typeof profile === 'string' || profile instanceof String) { if (typeof profile === 'string' || profile instanceof String) {
profile = {model: profile}; profile = {model: profile};
}
if (!profile.api) {
if (profile.model.includes('gemini')) if (profile.model.includes('gemini'))
profile.api = 'google'; profile.api = 'google';
else if (profile.model.includes('gpt') || profile.model.includes('o1')|| profile.model.includes('o3')) else if (profile.model.includes('gpt') || profile.model.includes('o1')|| profile.model.includes('o3'))
@ -110,7 +112,7 @@ export class Prompter {
profile.api = 'anthropic'; profile.api = 'anthropic';
else if (profile.model.includes('huggingface/')) else if (profile.model.includes('huggingface/'))
profile.api = "huggingface"; profile.api = "huggingface";
else if (profile.model.includes('meta/') || profile.model.includes('replicate/')) else if (profile.model.includes('replicate/'))
profile.api = 'replicate'; profile.api = 'replicate';
else if (profile.model.includes('mistralai/') || profile.model.includes("mistral/")) else if (profile.model.includes('mistralai/') || profile.model.includes("mistral/"))
model_profile.api = 'mistral'; model_profile.api = 'mistral';
@ -133,32 +135,31 @@ export class Prompter {
_createModel(profile) { _createModel(profile) {
let model = null; let model = null;
if (profile.api === 'google') if (profile.api === 'google')
model = new Gemini(profile.model, profile.url); model = new Gemini(profile.model, profile.url, profile.params);
else if (profile.api === 'openai') else if (profile.api === 'openai')
model = new GPT(profile.model, profile.url); model = new GPT(profile.model, profile.url, profile.params);
else if (profile.api === 'anthropic') else if (profile.api === 'anthropic')
model = new Claude(profile.model, profile.url); model = new Claude(profile.model, profile.url, profile.params);
else if (profile.api === 'replicate') else if (profile.api === 'replicate')
model = new ReplicateAPI(profile.model, profile.url); model = new ReplicateAPI(profile.model, profile.url, profile.params);
else if (profile.api === 'ollama') else if (profile.api === 'ollama')
model = new Local(profile.model, profile.url); model = new Local(profile.model, profile.url, profile.params);
else if (profile.api === 'mistral') else if (profile.api === 'mistral')
model = new Mistral(profile.model, profile.url); model = new Mistral(profile.model, profile.url, profile.params);
else if (profile.api === 'groq') { else if (profile.api === 'groq')
model = new GroqCloudAPI(profile.model.replace('groq/', '').replace('groqcloud/', ''), profile.url, max_tokens ? max_tokens : 8192); model = new GroqCloudAPI(profile.model.replace('groq/', '').replace('groqcloud/', ''), profile.url, profile.params);
}
else if (profile.api === 'huggingface') else if (profile.api === 'huggingface')
model = new HuggingFace(profile.model, profile.url); model = new HuggingFace(profile.model, profile.url, profile.params);
else if (profile.api === 'novita') else if (profile.api === 'novita')
model = new Novita(profile.model.replace('novita/', ''), profile.url); model = new Novita(profile.model.replace('novita/', ''), profile.url, profile.params);
else if (profile.api === 'qwen') else if (profile.api === 'qwen')
model = new Qwen(profile.model, profile.url); model = new Qwen(profile.model, profile.url, profile.params);
else if (profile.api === 'xai') else if (profile.api === 'xai')
model = new Grok(profile.model, profile.url); model = new Grok(profile.model, profile.url, profile.params);
else if (profile.api === 'deepseek') else if (profile.api === 'deepseek')
model = new DeepSeek(profile.model, profile.url); model = new DeepSeek(profile.model, profile.url, profile.params);
else else
throw new Error('Unknown API:', api); throw new Error('Unknown API:', profile.api);
return model; return model;
} }

View file

@ -4,8 +4,9 @@
import { getKey } from '../utils/keys.js'; import { getKey } from '../utils/keys.js';
export class Qwen { export class Qwen {
constructor(modelName, url) { constructor(model_name, url, params) {
this.modelName = modelName; this.model_name = model_name;
this.params = params;
this.url = url || 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation'; this.url = url || 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
this.apiKey = getKey('QWEN_API_KEY'); this.apiKey = getKey('QWEN_API_KEY');
} }
@ -19,7 +20,11 @@ export class Qwen {
const data = { const data = {
model: this.modelName || 'qwen-plus', model: this.modelName || 'qwen-plus',
input: { messages: [{ role: 'system', content: systemMessage }, ...turns] }, input: { messages: [{ role: 'system', content: systemMessage }, ...turns] },
parameters: { result_format: 'message', stop: stopSeq }, parameters: {
result_format: 'message',
stop: stopSeq,
...(this.params || {})
},
}; };
// Add default user message if all messages are 'system' role // Add default user message if all messages are 'system' role

View file

@ -4,9 +4,10 @@ import { getKey } from '../utils/keys.js';
// llama, mistral // llama, mistral
export class ReplicateAPI { export class ReplicateAPI {
constructor(model_name, url) { constructor(model_name, url, params) {
this.model_name = model_name; this.model_name = model_name;
this.url = url; this.url = url;
this.params = params;
if (this.url) { if (this.url) {
console.warn('Replicate API does not support custom URLs. Ignoring provided URL.'); console.warn('Replicate API does not support custom URLs. Ignoring provided URL.');
@ -22,7 +23,11 @@ export class ReplicateAPI {
const prompt = toSinglePrompt(turns, null, stop_seq); const prompt = toSinglePrompt(turns, null, stop_seq);
let model_name = this.model_name || 'meta/meta-llama-3-70b-instruct'; let model_name = this.model_name || 'meta/meta-llama-3-70b-instruct';
const input = { prompt, system_prompt: systemMessage }; const input = {
prompt,
system_prompt: systemMessage,
...(this.params || {})
};
let res = null; let res = null;
try { try {
console.log('Awaiting Replicate API response...'); console.log('Awaiting Replicate API response...');

View file

@ -57,11 +57,8 @@ const argv = yargs(args)
const agent = new Agent(); const agent = new Agent();
await agent.start(argv.profile, argv.load_memory, argv.init_message, argv.count_id, argv.task_path, argv.task_id); await agent.start(argv.profile, argv.load_memory, argv.init_message, argv.count_id, argv.task_path, argv.task_id);
} catch (error) { } catch (error) {
console.error('Failed to start agent process:', { console.error('Failed to start agent process:');
message: error.message || 'No error message', console.error(error);
stack: error.stack || 'No stack trace',
error: error
});
process.exit(1); process.exit(1);
} }
})(); })();

View file

@ -26,8 +26,10 @@ export function toSinglePrompt(turns, system=null, stop_seq='***', model_nicknam
return prompt; return prompt;
} }
// ensures stricter turn order for anthropic/llama models // ensures stricter turn order and roles:
// combines repeated messages from the same role, separates repeat assistant messages with filler user messages // - system messages are treated as user messages and prefixed with SYSTEM:
// - combines repeated messages from users
// - separates repeat assistant messages with filler user messages
export function strictFormat(turns) { export function strictFormat(turns) {
let prev_role = null; let prev_role = null;
let messages = []; let messages = [];