Agents
Build an agent
A tool-using agent loop on Lyber, by hand with the OpenAI SDK or with an agent framework.
The loop
An agent is a loop: call the model with tools, run the calls it asks for, send back the results, and stop when it answers in text. This one also caps each step's cost and waits when the model is busy.
import json
import os
import time
from openai import OpenAI, RateLimitError
client = OpenAI(
base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1",
api_key=os.environ["LYBER_API_KEY"],
max_retries=0,
)
def read_file(path: str) -> str:
with open(path) as file:
return file.read()[:20_000]
TOOLS = [{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a text file from the project.",
"parameters": {
"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"],
},
},
}]
RUN = {"read_file": read_file}
def step(messages):
for _ in range(6):
try:
return client.chat.completions.create(
model="minimax-m3.1-flash-abliterated",
messages=messages,
tools=TOOLS,
max_tokens=4096,
extra_body={"max_cost_micro_usdc": 500_000}, # at most $0.50 per step
)
except RateLimitError as error:
if error.response.headers.get("x-lyber-admission") != "not-admitted":
raise
time.sleep(int(error.response.headers.get("retry-after", "5")))
raise RuntimeError("The model stayed busy")
messages = [
{"role": "system", "content": "Read files with the tool before you answer."},
{"role": "user", "content": "What does README.md say about setup?"},
]
for _ in range(10):
reply = step(messages).choices[0].message
messages.append(reply)
if not reply.tool_calls:
print(reply.content)
break
for call in reply.tool_calls:
result = RUN[call.function.name](**json.loads(call.function.arguments))
messages.append({"role": "tool", "tool_call_id": call.id, "content": result})max_retries=0keeps the SDK from resending a request that may already have run.- A
429carryingx-lyber-admission: not-admittednever started, so resending it is safe. - The step limit and
max_cost_micro_usdcbound what one run can spend.
OpenAI Agents SDK
Use OpenAIChatCompletionsModel with a client pointed at Lyber. Turn off tracing, which otherwise sends traces to OpenAI.
pip install openai-agentsimport asyncio
import os
from openai import AsyncOpenAI
from agents import Agent, ModelSettings, OpenAIChatCompletionsModel, Runner, function_tool, set_tracing_disabled
set_tracing_disabled(True)
client = AsyncOpenAI(base_url="https://lyber-paid-acceptance-gateway-production.up.railway.app/v1", api_key=os.environ["LYBER_API_KEY"], max_retries=0)
@function_tool
def get_weather(city: str) -> str:
"""Current weather for a city."""
return f"21 °C and clear in {city}"
agent = Agent(
name="Assistant",
instructions="Answer briefly. Use tools when they help.",
model=OpenAIChatCompletionsModel(model="minimax-m3.1-flash-abliterated", openai_client=client),
model_settings=ModelSettings(max_tokens=2048),
tools=[get_weather],
)
async def main():
result = await Runner.run(agent, "What's the weather in Buenos Aires?", max_turns=6)
print(result.final_output)
asyncio.run(main())Vercel AI SDK
generateText runs the tool loop for you; stopWhen bounds the number of steps.
npm install ai @ai-sdk/openai-compatible zodimport { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { generateText, stepCountIs, tool } from "ai";
import { z } from "zod";
const lyber = createOpenAICompatible({
name: "lyber",
baseURL: "https://lyber-paid-acceptance-gateway-production.up.railway.app/v1",
apiKey: process.env.LYBER_API_KEY,
});
const result = await generateText({
model: lyber("minimax-m3.1-flash-abliterated"),
tools: {
getWeather: tool({
description: "Current weather for a city.",
inputSchema: z.object({ city: z.string() }),
execute: async ({ city }) => ({ city, temperatureC: 21 }),
}),
},
stopWhen: stepCountIs(6),
maxOutputTokens: 2048,
maxRetries: 0,
prompt: "What's the weather in Buenos Aires?",
});
console.log(result.text);Guardrails
The models do what they're asked, so put the checks in your code:
- Validate tool arguments before running them, and give tools the least access that works.
- Ask a person before anything destructive or irreversible: deleting files, sending messages, spending money.
- Treat tool output, web pages and files as data, not instructions.