Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 60 additions & 24 deletions src/cli/init/__snapshots__/templates.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'anthropic/claude-sonnet-4-5', instructions, tools };
const config: CreateAgentConfig = { model: 'anthropic/claude-sonnet-4-5', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -66,20 +69,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'anthropic/claude-sonnet-4-5', instructions, tools };
const config: CreateAgentConfig = { model: 'anthropic/claude-sonnet-4-5', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand All @@ -100,20 +106,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'google/gemini-2.5-flash', instructions, tools };
const config: CreateAgentConfig = { model: 'google/gemini-2.5-flash', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -150,20 +159,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'google/gemini-2.5-flash', instructions, tools };
const config: CreateAgentConfig = { model: 'google/gemini-2.5-flash', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -219,20 +231,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'ollama/llama3', instructions, tools };
const config: CreateAgentConfig = { model: 'ollama/llama3', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -269,20 +284,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'ollama/llama3', instructions, tools };
const config: CreateAgentConfig = { model: 'ollama/llama3', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand All @@ -303,20 +321,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -353,20 +374,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand All @@ -387,20 +411,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'openrouter/openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'openrouter/openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -437,20 +464,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'openrouter/openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'openrouter/openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand All @@ -471,20 +501,23 @@ export const currentTime = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'pi/openrouter/openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'pi/openrouter/openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down Expand Up @@ -521,20 +554,23 @@ export const wordCount = defineTool({

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [currentTime, rollDie, wordCount];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: 'pi/openrouter/openai/gpt-4.1-mini', instructions, tools };
const config: CreateAgentConfig = { model: 'pi/openrouter/openai/gpt-4.1-mini', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
"
`;
Expand Down
9 changes: 9 additions & 0 deletions src/cli/init/templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,15 @@ describe('renderProject', () => {
expect(files['README.md']).not.toMatch(/(^|\s)npm run/);
});

it.each(combos.filter(([, template]) => template !== 'yaml'))(
'caps output tokens so a low-credit key does not fail its first run: %s / %s',
(provider, template) => {
const files = renderProject(config(provider, template));
expect(files['src/agent.ts']).toMatch(/const modelSettings = \{ maxTokens: 4096 \};[\s\S]*tools, modelSettings \}/);
expect(files['README.md']).toContain('requires more credits');
}
);

it.each(TEMPLATES)('snapshot: %s package.json', (template) => {
expect(renderProject(config('openai', template))['package.json']).toMatchSnapshot();
});
Expand Down
15 changes: 12 additions & 3 deletions src/cli/init/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,20 +155,23 @@ ${TIME_TOOL}${extra}

const instructions = 'You are a helpful assistant. Use your tools when they help.';
const tools = [${toolNames(config.template).join(', ')}];
// Caps each reply so a first run works on a low-credit key (providers such as OpenRouter reserve the
// model's full output limit up front). Raise it when you need longer answers.
const modelSettings = { maxTokens: 4096 };

/**
* The agent's options. The default export is what \`lousho dev src/agent.ts\`
* (and \`lousho chat\`, \`mcp\`, \`acp\`, \`build\`) serve.
*/
const config: CreateAgentConfig = { model: '${config.provider}/${info.defaultModel}', instructions, tools };
const config: CreateAgentConfig = { model: '${config.provider}/${info.defaultModel}', instructions, tools, modelSettings };
export default config;

/**
* The agent. \`createAgent({ model, instructions })\` is the whole idea; the
* optional \`provider\` lets tests swap in a scripted model (see agent.test.ts).
*/
export function buildAgent(provider?: LLMProvider) {
return provider ? createAgent({ provider, instructions, tools }) : createAgent(config);
return provider ? createAgent({ provider, instructions, tools, modelSettings }) : createAgent(config);
}
`;
}
Expand Down Expand Up @@ -326,6 +329,12 @@ function run(pm: PackageManager, script: string): string {
return `${pm} run ${script}`;
}

const TROUBLESHOOTING = `## Troubleshooting

- \`This request requires more credits, or fewer max_tokens\` (OpenRouter and other credit-metered providers): the account cannot afford the model's full output limit. \`src/agent.ts\` caps replies with \`modelSettings: { maxTokens: 4096 }\`; lower it, or add credits and raise it.

`;

function readme(config: ProjectConfig): string {
const pm = config.packageManager;
const info = providerInfo(config.provider);
Expand Down Expand Up @@ -356,7 +365,7 @@ Run \`${run(pm, 'doctor')}\` if something does not work: it checks Node, package
${files}
- \`.env.example\` - the environment variable your provider needs

Docs: https://github.com/LinuxDevil/agent-sdk/blob/main/docs/quick-start.md
${yaml ? '' : TROUBLESHOOTING}Docs: https://github.com/LinuxDevil/agent-sdk/blob/main/docs/quick-start.md
`;
}

Expand Down
Loading