Skip to content

Instantly share code, notes, and snippets.

@beardedtim
Created March 13, 2026 11:29
Show Gist options
  • Select an option

  • Save beardedtim/0d7f33af2999f5eedb6fd4d64f2e0095 to your computer and use it in GitHub Desktop.

Select an option

Save beardedtim/0d7f33af2999f5eedb6fd4d64f2e0095 to your computer and use it in GitHub Desktop.
A basic agent using Together.AI
import { defaultClient } from "./together";
import Log from "./log";
import { ToolSchema } from "./tools/schemas";
interface ToolDef {
schema: ToolSchema;
execute: (args: unknown) => AsyncGenerator<any, unknown>;
}
export interface AgentConfig {
model: string;
prompt: string;
tools: {
[x: string]: ToolDef;
};
client?: typeof defaultClient;
logger?: typeof Log;
forceInitialToolCall?: boolean;
maxIterations?: number;
}
export interface ActRequest {
query: string;
}
export type AgentEvent =
| {
type: "agent_start";
query: string;
}
| {
type: "model_stream";
delta: string;
}
| {
type: "tool_call";
id: string;
name: string;
args: unknown;
}
| {
type: "tool_result";
id: string;
name: string;
result: unknown;
}
| {
type: "agent_thought";
content: string;
}
| {
type: "final_answer";
content: string;
}
| {
type: "error";
message: string;
}
| {
type: "agent_complete";
};
export class Agent {
#model: string;
#agent_prompt: string;
#available_tools: { [x: string]: ToolDef };
#client: typeof defaultClient;
#log: typeof Log;
#forceInitialToolCall: boolean;
#maxAgentLoops: number;
constructor(config: AgentConfig) {
this.#model = config.model;
this.#agent_prompt = config.prompt;
this.#available_tools = config.tools;
this.#client = config.client ?? defaultClient;
this.#log = config.logger ?? Log.child({ name: `Agent::${this.#model}` });
this.#forceInitialToolCall = config.forceInitialToolCall ?? false;
this.#maxAgentLoops = config.maxIterations ?? 10;
}
#parseToolCall(functionCall: {
name: string;
arguments: string;
id: string;
}) {
return {
name: functionCall.name,
args: JSON.parse(functionCall.arguments),
id: functionCall.id,
};
}
async *act(request: ActRequest): AsyncGenerator<AgentEvent> {
yield {
type: "agent_start",
query: request.query,
};
const messageHistory: any[] = [
{ role: "system", content: this.#agent_prompt },
{ role: "user", content: request.query },
];
let iteration = 0;
while (iteration < this.#maxAgentLoops) {
iteration++;
const completion = await this.#client.chat.completions.create({
model: this.#model,
messages: messageHistory,
tools: Object.values(this.#available_tools).map((t) => t.schema),
stream: true,
});
const toolCalls: { name: string; arguments: string; id: string }[] = [];
let streamedText = "";
for await (const chunk of completion) {
const [choice] = chunk.choices;
if (!choice) continue;
const delta = choice.delta;
if (delta?.content) {
streamedText += delta.content;
yield {
type: "model_stream",
delta: delta.content,
};
}
if (delta?.tool_calls) {
for (const tool_call of delta.tool_calls) {
if (tool_call.id === null && tool_call.function?.arguments) {
toolCalls[toolCalls.length - 1].arguments +=
tool_call.function.arguments;
} else {
toolCalls.push({
name: tool_call.function?.name ?? "",
arguments: tool_call.function?.arguments ?? "",
id: tool_call.id!,
});
}
}
}
}
// If model just answered
if (!toolCalls.length) {
if (streamedText.trim()) {
yield {
type: "final_answer",
content: streamedText,
};
}
yield { type: "agent_complete" };
return;
}
const parsed = toolCalls.map(this.#parseToolCall);
if (streamedText.trim()) {
messageHistory.push({
role: "assistant",
content: streamedText,
});
}
messageHistory.push({
role: "assistant",
tool_calls: parsed.map((call) => ({
id: call.id,
type: "function",
function: {
name: call.name,
arguments: JSON.stringify(call.args),
},
})),
});
for (const call of parsed) {
yield {
type: "tool_call",
id: call.id,
name: call.name,
args: call.args,
};
const tool = this.#available_tools[call.name];
if (!tool) {
yield {
type: "error",
message: `Unknown tool ${call.name}`,
};
continue;
}
const result = yield* tool.execute(call.args);
yield {
type: "tool_result",
id: call.id,
name: call.name,
result,
};
messageHistory.push({
role: "tool",
tool_call_id: call.id,
name: call.name,
content: JSON.stringify(result),
});
}
}
yield {
type: "error",
message: "Agent reached max iterations",
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment