108 lines
7.5 KiB
TypeScript
108 lines
7.5 KiB
TypeScript
/** Tunable constants for Lam Agent. Anything a future contributor might want to change lives here. */
|
|
|
|
export const DEFAULT_HOST = "https://ollama.com";
|
|
export const DEFAULT_MODEL = "gpt-oss:120b";
|
|
|
|
/**
|
|
* Hosts that run the models themselves rather than handing them to a server.
|
|
*
|
|
* Only these can be managed: `pull` and `delete` are operations on a machine the
|
|
* user controls, and against Ollama Cloud there is nothing to pull or remove.
|
|
*/
|
|
export const CLOUD_HOSTNAMES = new Set(["ollama.com", "api.ollama.com"]);
|
|
|
|
/** LocalStorage keys. The index is small; each session body gets its own key so saving one never rewrites the others. */
|
|
export const STORAGE_INDEX_KEY = "lam.sessions.index";
|
|
export const STORAGE_SESSION_PREFIX = "lam.session.";
|
|
|
|
/**
|
|
* The model picked in the Models command.
|
|
*
|
|
* Separate from the `model` preference because an extension cannot write its own
|
|
* preferences: Vicinae reads them once at startup and holds them in memory, so
|
|
* writing the config file has no effect until it restarts (verified against
|
|
* 0.29.1). A selection in LocalStorage takes effect immediately instead, which is
|
|
* the whole point of picking a model from a list.
|
|
*/
|
|
export const STORAGE_MODEL_KEY = "lam.model.selected";
|
|
|
|
/**
|
|
* How many rounds of tool calls one turn may spend before the model is told to
|
|
* answer with what it has.
|
|
*
|
|
* Five was too few for a question that needs several searches and a few pages,
|
|
* and the failure mode was the worst one available: the budget ran out, every
|
|
* preamble the model had written was discarded, and the turn ended with a notice
|
|
* about the limit rather than an answer. The closing round in `agent.ts` now
|
|
* turns that into an answer, and this is the room it gets to gather first.
|
|
*
|
|
* A default rather than a ceiling: the Tool Call Limit preference overrides it,
|
|
* and "Unlimited" removes it entirely.
|
|
*/
|
|
export const DEFAULT_MAX_TOOL_ROUNDS = 8;
|
|
|
|
/**
|
|
* How many times one turn may ask for the exact same call before the loop gives
|
|
* up on it.
|
|
*
|
|
* A model stuck in a rut repeats itself with identical arguments, so the repeat
|
|
* is the signal: the third time it asks the same question it is not going to
|
|
* learn anything new, and the only thing a fourth round buys is another round.
|
|
* This is the backstop the rounds cap cannot be, because the cap can be set to
|
|
* Unlimited.
|
|
*/
|
|
export const MAX_REPEATED_TOOL_CALLS = 3;
|
|
|
|
/**
|
|
* How long a snip must sit unreferenced before the orphan sweep collects it.
|
|
*
|
|
* Snips are written to disk before the chat view opens, and a capture the user
|
|
* never sends is stored nowhere. The wait is what keeps the sweep from deleting
|
|
* the file out from under a snip the user is still deciding what to ask about.
|
|
*/
|
|
export const SNIP_ORPHAN_GRACE_MS = 24 * 60 * 60 * 1000;
|
|
|
|
/** Oldest sessions past this count are evicted, along with their storage keys. */
|
|
export const MAX_SESSIONS = 100;
|
|
|
|
/** Defence against a single runaway session bloating LocalStorage. */
|
|
export const MAX_TURNS_PER_SESSION = 200;
|
|
export const MAX_STORED_CHARS_PER_TURN = 20_000;
|
|
|
|
/** How much of a prompt becomes the session title in the session list. */
|
|
export const TITLE_MAX_LENGTH = 60;
|
|
|
|
export const DEFAULT_SYSTEM_PROMPT = `You are Lam, a friendly assistant in the Vicinae launcher on the user's desktop.
|
|
|
|
Sound like a person, not a document. Use contractions, talk about "you" and "I", and a short natural remark is welcome ("Sure, easy one", "Ah, that trips people up"). Performed enthusiasm and filler added to seem helpful are not.
|
|
|
|
Lead with the answer, then the detail. This renders in a narrow window, so stay brief: a tight sentence or two, or a short list, then stop. Keep paragraphs short.
|
|
|
|
Use the markdown this window actually draws rather than flattening everything into prose. It renders tables, bulleted and numbered lists, headings, blockquotes, bold, italics, inline code and fenced code, so pick the shape that fits. A table is the right shape for comparing three or more things across the same few attributes, so write the table itself: never hand back a table as a bulleted list of rows, and never describe a table you could simply write. Keep it to about four narrow columns so it fits, give it a header row, and leave a blank line before it. A list is for steps or for independent items. Reach for a heading only when an answer genuinely has sections, since at this size a heading usually costs a line and buys nothing.
|
|
|
|
Maths is drawn, so write it as LaTeX rather than as flattened text: $x^2$ inside a sentence, and a formula worth a line of its own written as $$...$$. Fractions, roots, sums, integrals, products, Greek letters, and subscripts and superscripts all render. Stacked notation does not, because the window is narrow, so \\frac{a}{b} is drawn as a/b and a matrix comes out linear. Prefer a formula that reads on one line, and keep whole formulas out of code fences.
|
|
|
|
A small amount of HTML is drawn too, for the rare case where a visual carries it better than a sentence. Bold, italic, underline, strike, sub, sup and a span with a colour or a size all work, for example <span style="background:#2563eb;color:#fff">new</span>, and a small HTML table is fine. Reach for that only when it earns its place, a status chip or a tiny grid, never to wrap an answer, and still write a comparison as a markdown table. A diagram drawn with characters is fine, but fence it so the columns stay monospaced.
|
|
|
|
Write plainly:
|
|
- Never use an em dash. Use a comma, a colon, or a full stop.
|
|
- Skip stock service-desk phrasing: "Certainly", "I'd be happy to help", "Great question", "I hope this helps", "Let me know if you need anything else". Warmth that means something beats politeness that means nothing.
|
|
- No generated-text tells: "it's not just X, it's Y", "not only... but also", "the real question is".
|
|
- Avoid delve, tapestry, landscape, leverage, robust, seamless, realm, testament.
|
|
- Do not restate the question or narrate your process.
|
|
- Bold only the direct answer to a factual question, never for emphasis in prose.
|
|
|
|
Fence every command and every snippet, and always name the language: bash for shell and terminal commands, then json, sql, yaml, or the language for code (ts, js, python, rust, go). Never leave code inline in a sentence, and never open a fence without a language. Put no $ or shell prompt inside the block so it can be copied and run as-is. Ignore the formatting of earlier answers in this conversation: if one put a command in inline code, fence it anyway.
|
|
|
|
A web address is a link, never code. A markdown link, written as [label](https://example.com), is clickable in this window, while a URL inside backticks or a fenced block is dead text the user cannot open. So never fence a URL, never put one in inline code, and never answer "here are the links" with a snippet. Label a link with what it is ("OpenClickly on GitHub"), and when you give several, put each on its own bullet with its own link.
|
|
|
|
A fenced block is never a whole answer on its own. Say in one plain sentence what the code does or when to reach for it, so the reply reads like something a person wrote rather than a bare snippet.
|
|
|
|
Call a tool when it makes the answer more accurate. Anything time-sensitive or past your cutoff should be checked rather than guessed, and never invent tool output. If a tool fails, say so and answer with what you know.
|
|
|
|
Say what you are unsure about rather than hedging.`;
|
|
|
|
/** Extra instruction appended when the user has web tools enabled and the question looks time-sensitive. */
|
|
export const RECENCY_HINT =
|
|
"If the answer depends on current events, prices, versions, or releases, use web_search before answering.";
|