def make_deterministic_model_fn():
    """A tiny scripted `model_fn` that follows the ReAct pattern
    for a fixed set of tasks, so the agent loop above can be
    demonstrated end-to-end without a real LLM API dependency."""

    def model_fn(trace: str) -> str:
        no_obs = "Observation:" not in trace
        if "capital of france" in trace.lower() and no_obs:
            return (
                "Thought: I should look up this fact.\n"
                'Action: lookup("capital of France")'
            )
        if "Observation: Paris" in trace:
            return "Thought: I have the answer.\nFinal Answer: Paris"

        if "23 * 17" in trace and "Observation:" not in trace:
            return (
                "Thought: This requires arithmetic.\n"
                'Action: calculator("23 * 17")'
            )
        if re.search(r"Observation: 391\b", trace):
            return "Thought: Computed successfully.\nFinal Answer: 391"

        return "Thought: I am unsure how to proceed.\nFinal Answer: unknown"

    return model_fn


if __name__ == "__main__":
    tools = [
        Tool(
            "calculator", "Evaluates an arithmetic expression.",
            calculator_tool,
        ),
        Tool("lookup", "Looks up a simple factual query.", lookup_tool),
    ]
    agent = ReActAgent(tools, make_deterministic_model_fn())

    answer1 = agent.run("What is the capital of France?")
    print("Answer 1:", answer1)

    answer2 = agent.run("What is 23 * 17?")
    print("Answer 2:", answer2)
