How Twiga Works
Tools are how the model does things it cannot do by writing text. Adding one is mostly registration.
Twiga ships with five:
| Tool | What it does |
|---|---|
search_knowledge | Retrieves passages from the teacher's textbooks. |
create_lesson_plan | Builds a structured lesson plan. |
generate_exercise | Produces practice questions. |
solve_equation | Works through mathematics, on its own model configuration. |
generate_necta_style_exam | Produces an exam in the national format, delivered as a PDF. |
app/tools/tool_code/your_tool/main.py with an __init__.py beside it. Export one async function named after the tool. Return a string, which is what the model sees as the result.ToolName enum in app/tools/registry.py. The member name and its string value must match.TOOL_FUNCTION_MAP in the same file.TOOLS_METADATA. This is what the model reads to decide whether to call your tool, so the description is doing real work. Be specific about when it applies.tools.your_tool.notification to app/assets/strings/english.yml. While the tool runs, the teacher gets this message so the wait does not look like a hang. If the key is missing, nothing is sent and a warning is logged.Some arguments come from the server, not the conversation. A user ID is the obvious case: you never want the model deciding whose data to read.
Add a builder to app/tools/internal_args.pyand it will be merged in after the model's arguments and before the call. Keep those parameters out of the JSON schema entirely.
If your tool works on one of the teacher's classes, name the parameter class_id and copy the shape used by the existing tools. The registry rewrites that property per request, injecting the real class IDs for that teacher as an enum. The model then cannot ask for a class the teacher does not teach.
| If your tool needs | Do this |
|---|---|
| A different model or provider | Add an entry to ToolSettings in app/config.py and a matching block under tools: in app/assets/config/base.yml. Follow solve_equation. |
| Its own prompt | Add files under app/assets/prompts/your_prompt/ and register the version in app/assets/config/prompts.yml. |
A declared prompt with no file crashes at import
The prompt manager reads its registry when the module loads, so a version listed inprompts.yml without a matching file stops the whole application from starting. It is a fast failure, but the traceback points at import machinery rather than at your typo.tests/tools/. The existing ones show the pattern.