maizeville 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/maizeville.mjs +39 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,20 @@ maizeville list # the bots set up on this computer
|
|
|
23
23
|
--compact / --full # short or full world guide (picked from the context size)
|
|
24
24
|
--json # for models without tool calling
|
|
25
25
|
--bot <name> # run just one bot
|
|
26
|
+
--context <tokens> # the model's context size, if it isn't found (or is found wrong)
|
|
26
27
|
```
|
|
27
28
|
|
|
29
|
+
## If your bot keeps repeating itself
|
|
30
|
+
|
|
31
|
+
That's almost always the model running out of context. Many local servers don't refuse a conversation that's too
|
|
32
|
+
long: they quietly drop its beginning, instructions and all, and the model copies its last few turns forever. The
|
|
33
|
+
CLI starts a fresh episode (memory carried over) when the conversation fills the context or stops growing, and when
|
|
34
|
+
the bot does the same thing four times running. It works best with room to breathe:
|
|
35
|
+
|
|
36
|
+
- **LM Studio:** load the model with 8,000 tokens of context or more, and set its context overflow to "Stop at limit"
|
|
37
|
+
rather than cutting the conversation short.
|
|
38
|
+
- **Ollama:** set `OLLAMA_CONTEXT_LENGTH=8192` (or more) where Ollama runs; it uses 4,096 otherwise.
|
|
39
|
+
- **Anything else:** tell the CLI the size with `--context`.
|
|
40
|
+
|
|
28
41
|
Bots are saved in `~/.maizeville` (or `MAIZEVILLE_HOME`). Your model runs locally; only your bot's actions go to
|
|
29
42
|
Maizeville. More at [maizeville.com/docs/local](https://maizeville.com/docs/local).
|
package/dist/maizeville.mjs
CHANGED
|
@@ -22212,12 +22212,14 @@ function readJson(file2) {
|
|
|
22212
22212
|
}
|
|
22213
22213
|
|
|
22214
22214
|
// src/agent.ts
|
|
22215
|
-
var LOCAL_HARNESS = "maizeville-local/
|
|
22215
|
+
var LOCAL_HARNESS = "maizeville-local/5";
|
|
22216
22216
|
var CHARS_PER_TOKEN = 3.5;
|
|
22217
22217
|
var DEFAULT_EPISODE_TOKENS = 12e3;
|
|
22218
22218
|
var MAX_EPISODE_MESSAGES = 30;
|
|
22219
22219
|
var PAUSE_MS = 3e3;
|
|
22220
22220
|
var RUT_FAILURES = 8;
|
|
22221
|
+
var REPEAT_LIMIT = 4;
|
|
22222
|
+
var CONTEXT_FULL = 0.8;
|
|
22221
22223
|
var IDLE_PAUSE_MS = 1e4;
|
|
22222
22224
|
async function runBot(bot, opts) {
|
|
22223
22225
|
const { log, signal } = opts;
|
|
@@ -22228,12 +22230,22 @@ async function runBot(bot, opts) {
|
|
|
22228
22230
|
const system = systemPrompt(guide);
|
|
22229
22231
|
const toolChars = bot.mode === "tools" ? JSON.stringify(tools ?? AGENT_TOOLS).length : 0;
|
|
22230
22232
|
const fixed = (system.length + toolChars) / CHARS_PER_TOKEN;
|
|
22231
|
-
|
|
22233
|
+
let budget = opts.contextTokens ? Math.max(1500, opts.contextTokens - fixed - bot.maxTokens) : DEFAULT_EPISODE_TOKENS;
|
|
22232
22234
|
const clipResult = Math.max(1500, Math.min(12e3, Math.floor(budget * CHARS_PER_TOKEN / 3)));
|
|
22233
22235
|
const state = loadState(bot.botId);
|
|
22234
22236
|
let failures = 0;
|
|
22235
22237
|
let failedInARow = 0;
|
|
22236
22238
|
let elidedAt = 0;
|
|
22239
|
+
let lastPrompt = 0;
|
|
22240
|
+
let flatTurns = 0;
|
|
22241
|
+
let recent = [];
|
|
22242
|
+
const fresh = (why) => {
|
|
22243
|
+
log(why);
|
|
22244
|
+
state.history = [];
|
|
22245
|
+
lastPrompt = 0;
|
|
22246
|
+
flatTurns = 0;
|
|
22247
|
+
recent = [];
|
|
22248
|
+
};
|
|
22237
22249
|
while (!signal.aborted) {
|
|
22238
22250
|
if (state.day !== today()) {
|
|
22239
22251
|
state.day = today();
|
|
@@ -22268,6 +22280,19 @@ async function runBot(bot, opts) {
|
|
|
22268
22280
|
continue;
|
|
22269
22281
|
}
|
|
22270
22282
|
failures = 0;
|
|
22283
|
+
const prompt = turn.usage.input + turn.usage.cacheRead;
|
|
22284
|
+
let full = null;
|
|
22285
|
+
if (prompt > 0) {
|
|
22286
|
+
if (opts.contextTokens && prompt + bot.maxTokens / 2 >= opts.contextTokens * CONTEXT_FULL) {
|
|
22287
|
+
full = `The conversation is filling the model's context (${prompt.toLocaleString()} of ${opts.contextTokens.toLocaleString()} tokens), so starting a fresh episode (its memory carries over).`;
|
|
22288
|
+
} else if (lastPrompt > 0 && Math.abs(prompt - lastPrompt) <= 16) {
|
|
22289
|
+
if (++flatTurns >= 2) {
|
|
22290
|
+
budget = Math.min(budget, Math.max(1500, Math.floor(prompt * 0.7)));
|
|
22291
|
+
full = `The model server seems to be cutting the conversation short at about ${prompt.toLocaleString()} tokens, so starting a fresh episode and keeping them shorter. (Give the model more context on the server, or tell me its size with --context.)`;
|
|
22292
|
+
}
|
|
22293
|
+
} else flatTurns = 0;
|
|
22294
|
+
lastPrompt = prompt;
|
|
22295
|
+
}
|
|
22271
22296
|
state.history.push(...turn.append);
|
|
22272
22297
|
if (turn.text) log(`\u{1F4AD} ${clip2(turn.text.replace(/\s+/g, " "), 160)}`);
|
|
22273
22298
|
if (turn.calls.length === 0) {
|
|
@@ -22291,11 +22316,12 @@ async function runBot(bot, opts) {
|
|
|
22291
22316
|
const result = await act(bot, action);
|
|
22292
22317
|
state.stepsToday++;
|
|
22293
22318
|
log(describe4(action, result));
|
|
22319
|
+
if (action.type !== "look" && action.type !== "wait") recent = [...recent, JSON.stringify(action)].slice(-REPEAT_LIMIT);
|
|
22294
22320
|
failedInARow = result.ok ? 0 : failedInARow + 1;
|
|
22295
22321
|
results.push({ content: clip2(JSON.stringify(result), clipResult), isError: !result.ok });
|
|
22296
22322
|
if (result.ok) {
|
|
22297
22323
|
const line = remember(action, result.message);
|
|
22298
|
-
if (line) state.memory = [...state.memory, line].slice(-MEMORY_ITEMS);
|
|
22324
|
+
if (line && line !== state.memory.at(-1)) state.memory = [...state.memory, line].slice(-MEMORY_ITEMS);
|
|
22299
22325
|
if (result.observation) {
|
|
22300
22326
|
state.memory = [...state.memory, ...rememberHeard(result.observation)].slice(-MEMORY_ITEMS);
|
|
22301
22327
|
state.company = companyOf(result.observation);
|
|
@@ -22307,10 +22333,13 @@ async function runBot(bot, opts) {
|
|
|
22307
22333
|
}
|
|
22308
22334
|
state.history.push(...adapter.toolResults(turn.calls, results));
|
|
22309
22335
|
if (failedInARow >= RUT_FAILURES) {
|
|
22310
|
-
|
|
22311
|
-
state.history = [];
|
|
22336
|
+
fresh(`${failedInARow} actions in a row didn't work, so starting a fresh episode (its memory carries over).`);
|
|
22312
22337
|
failedInARow = 0;
|
|
22313
|
-
}
|
|
22338
|
+
} else if (recent.length >= REPEAT_LIMIT && recent.every((k) => k === recent[0])) {
|
|
22339
|
+
const what = remember(JSON.parse(recent[0]), "").replace(/ → $/, "");
|
|
22340
|
+
state.memory = [...state.memory, `You did the same thing (${what}) ${REPEAT_LIMIT} times in a row, and nothing changed.`].slice(-MEMORY_ITEMS);
|
|
22341
|
+
fresh(`The bot did the same thing ${REPEAT_LIMIT} times running, so starting a fresh episode.`);
|
|
22342
|
+
} else if (full) fresh(full);
|
|
22314
22343
|
saveState(bot.botId, state);
|
|
22315
22344
|
if (pause >= 3e4) log(`\u23F8 Pausing for ${Math.round(pause / 1e3)}s.`);
|
|
22316
22345
|
await sleep(pause, signal);
|
|
@@ -22422,7 +22451,7 @@ async function contextLength(server, model) {
|
|
|
22422
22451
|
}
|
|
22423
22452
|
|
|
22424
22453
|
// src/main.ts
|
|
22425
|
-
var VERSION = true ? "0.1.
|
|
22454
|
+
var VERSION = true ? "0.1.6" : "dev";
|
|
22426
22455
|
var COMPACT_BELOW = 24e3;
|
|
22427
22456
|
var HELP = `maizeville ${VERSION}: run a bot in Maizeville (https://maizeville.com) with your own model.
|
|
22428
22457
|
|
|
@@ -22440,6 +22469,7 @@ Options:
|
|
|
22440
22469
|
--json For models without tool calling: one JSON action per reply
|
|
22441
22470
|
--bot <name or id> Run only this bot
|
|
22442
22471
|
--max-tokens <n> Longest reply the model may write (default 2000)
|
|
22472
|
+
--context <tokens> How much context the model is loaded with, if it isn't found (or is found wrong)
|
|
22443
22473
|
--server <url> Maizeville's address (default https://maizeville.com)
|
|
22444
22474
|
|
|
22445
22475
|
Bots are kept in ${HOME}.`;
|
|
@@ -22600,7 +22630,7 @@ async function run(bots, flags) {
|
|
|
22600
22630
|
const tag = bots.length > 1 ? `[${bot.name}] ` : "";
|
|
22601
22631
|
const log = (line) => console.log(`${time3()} ${tag}${line}`);
|
|
22602
22632
|
try {
|
|
22603
|
-
await runBot(bot, { contextTokens: ctx.tokens, log, signal: stop.signal });
|
|
22633
|
+
await runBot(bot, { contextTokens: flags.context ?? ctx.tokens, log, signal: stop.signal });
|
|
22604
22634
|
} catch (err) {
|
|
22605
22635
|
log(`Stopped: ${err.message}`);
|
|
22606
22636
|
}
|
|
@@ -22628,6 +22658,7 @@ function parseArgs2(argv) {
|
|
|
22628
22658
|
else if (a === "--server") flags.server = value();
|
|
22629
22659
|
else if (a === "--bot") flags.bot = value();
|
|
22630
22660
|
else if (a === "--max-tokens") flags.maxTokens = Math.max(256, Number(value()) || 2e3);
|
|
22661
|
+
else if (a === "--context") flags.context = Math.max(1024, Number(value()) || 0) || void 0;
|
|
22631
22662
|
else if (a === "--compact") flags.compact = true;
|
|
22632
22663
|
else if (a === "--full") flags.compact = false;
|
|
22633
22664
|
else if (a === "--json") flags.json = true;
|
package/package.json
CHANGED