Last reviewed: August 30, 2026
Direct answer
An Agno CometAPI integration can use Agno’s official CometAPI model provider, so you do not need to write a custom gateway adapter. The reliable sequence is to place the CometAPI key in the COMETAPI_KEY environment variable, call get_available_models(), select an ID returned for the current account, and pass that ID to CometAPI. Then register a typed Python function in the agent’s tools list and run a prompt that requires the function’s data.
A plausible final answer is not enough to prove tool calling. The example below records a sanitized event inside the function and fails the run when the function was never invoked. That gives you separate evidence for model discovery, model selection, tool execution, and completion of the agent loop.
The CometAPI Agno integration guide lists Python 3.10 or newer, the Agno package, an active CometAPI key, and a current model ID as the relevant prerequisites. Install the documented packages and read the key without echoing it:
python -m pip install -U agno openai
read -rsp "CometAPI key: " COMETAPI_KEY
echo
export COMETAPI_KEY
Keep that terminal open. An environment variable exported in one shell is not automatically available in a different shell.
Who this is for
This tutorial is for Python developers who already understand functions and environment variables but are new to Agno’s model and tool abstractions. It is especially useful when you need a small, reproducible proof that CometAPI accepted a current model ID and that the selected model completed Agno’s function-tool loop.
The example deliberately uses a read-only local fixture. It does not modify files, call business systems, or perform a consequential action. That makes the first test easier to inspect and repeat before you connect a real data source.
Key takeaways
- Import
CometAPIfromagno.models.cometapi; no custom provider class is needed. - Discover model IDs at run time instead of copying an unverified ID from an old example.
- Treat catalog membership and tool support as two different checks. A listed model is available to the account, but the successful tool event is the proof required by this tutorial.
- Give a Python tool a specific name, a concise docstring, typed arguments, and structured output.
- Log stage, outcome, model ID, tool name, and error type, but never log the CometAPI key or authentication material.
- Exercise an intentional error path before treating the integration as operational.
Sources checked
- Use Agno with CometAPI
documents the official provider import,
COMETAPI_KEY, model discovery, the default CometAPI gateway configuration, and the basic agent run. - Agno’s CometAPI provider reference
confirms the provider parameters, package installation, environment-based authentication, and
get_available_models()method. - Agno’s tools overview explains how a function’s name, docstring, and type hints become a tool definition and how Agno executes a requested tool before returning its result to the model.
Contract details to verify
Build one script for discovery and execution
Save the following as agent.py. The local SERVICE_WINDOWS mapping is test data, not a description of any CometAPI service level. The function only reads that mapping. Its own structured event is the decisive evidence that Agno invoked it.
from __future__ import annotations
import json
import os
import sys
from uuid import uuid4
from agno.agent import Agent
from agno.models.cometapi import CometAPI
RUN_ID = uuid4().hex[:12]
TOOL_CALL_COUNT = 0
SERVICE_WINDOWS: dict[str, dict[str, str]] = {
'billing': {
'response_target': '4 business hours',
'coverage': 'weekday demo fixture',
},
'catalog': {
'response_target': '2 business hours',
'coverage': 'daily demo fixture',
},
}
def emit_event(stage: str, outcome: str, **fields: object) -> None:
payload: dict[str, object] = {
'run_id': RUN_ID,
'stage': stage,
'outcome': outcome,
}
payload.update(fields)
print(json.dumps(payload, sort_keys=True))
def lookup_service_window(service: str) -> dict[str, str]:
'''Read a service window from the local demonstration fixture.'''
global TOOL_CALL_COUNT
TOOL_CALL_COUNT += 1
record = SERVICE_WINDOWS.get(service)
if record is None:
emit_event(
'tool_call',
'not_found',
tool_name='lookup_service_window',
service=service,
)
return {'service': service, 'status': 'not_found'}
emit_event(
'tool_call',
'found',
tool_name='lookup_service_window',
service=service,
)
return {'service': service, 'status': 'found', **record}
def main() -> int:
if not os.environ.get('COMETAPI_KEY'):
emit_event(
'configuration',
'error',
error_type='missing_environment_variable',
)
return 2
gateway = CometAPI()
try:
available_models = gateway.get_available_models()
except Exception as exc:
emit_event(
'model_catalog',
'error',
error_type=type(exc).__name__,
)
return 1
if not isinstance(available_models, list) or not available_models:
emit_event(
'model_catalog',
'error',
error_type='empty_or_invalid_catalog',
)
return 1
if any(not isinstance(item, str) for item in available_models):
emit_event(
'model_catalog',
'error',
error_type='unexpected_catalog_shape',
)
return 1
emit_event(
'model_catalog',
'success',
catalog_count=len(available_models),
)
if '--list-models' in sys.argv:
for model_id in available_models:
print(model_id)
return 0
selected_model = os.environ.get('COMETAPI_MODEL', '')
if not selected_model:
emit_event(
'model_selection',
'error',
error_type='missing_model_selection',
)
return 2
if selected_model not in available_models:
emit_event(
'model_selection',
'error',
model_id=selected_model,
error_type='stale_or_unknown_model',
)
return 2
emit_event(
'model_selection',
'success',
model_id=selected_model,
)
agent = Agent(
model=CometAPI(id=selected_model),
tools=[lookup_service_window],
instructions=(
'For service-window questions, always call '
'lookup_service_window and report only its returned fixture.'
),
)
try:
agent.print_response(
'Use lookup_service_window to report the response target for billing.'
)
except Exception as exc:
emit_event(
'agent_run',
'error',
model_id=selected_model,
error_type=type(exc).__name__,
)
return 1
if TOOL_CALL_COUNT < 1:
emit_event(
'tool_verification',
'error',
model_id=selected_model,
error_type='required_tool_was_not_called',
)
return 1
emit_event(
'agent_run',
'success',
model_id=selected_model,
tool_call_count=TOOL_CALL_COUNT,
)
return 0
if __name__ == '__main__':
raise SystemExit(main())
Agno’s provider documentation says CometAPI reads COMETAPI_KEY and supplies the documented CometAPI /v1 gateway base by default. The script relies on that provider default instead of duplicating endpoint configuration. It still sets the model ID explicitly because the purpose of this workflow is to validate a currently returned ID rather than rely on a library default.
Run the happy path
First retrieve the model list:
python agent.py --list-models
A successful discovery run emits a model_catalog event with outcome set to success, followed by model IDs. Choose an ID that appears in that output and that is documented for function-tool use in your current model selection context. Catalog discovery verifies availability; it does not, by itself, prove tool capability.
Put the selected non-secret model ID in a separate environment variable and run the fixed test:
export COMETAPI_MODEL="selected-model-id"
python agent.py
The happy path must contain all four signals:
model_catalogreportssuccesswith a positivecatalog_count.model_selectionreportssuccessfor the chosenmodel_id.tool_callreportsfoundforlookup_service_windowandbilling.agent_runreportssuccesswith atool_call_countof at least one.
The model’s prose is secondary. If it states the fixture value but no tool_call event appears, this workflow treats the run as a failure because there is no evidence that the answer came from the function.
Exercise the error path
Use a one-command override to test stale model handling without changing the valid value stored in your shell:
COMETAPI_MODEL="not-in-catalog" python agent.py
After catalog discovery, the script should stop before creating the agent and emit an event with this shape:
{"error_type":"stale_or_unknown_model","model_id":"not-in-catalog","outcome":"error","run_id":"local-run","stage":"model_selection"}
That controlled failure proves that an outdated or mistyped ID cannot silently fall through to a generation request. Restore a returned model ID and rerun the happy path afterward.
Keep the operational log sanitized
The deliberate log fields are run_id, stage, outcome, catalog_count, model_id, tool_name, service, tool_call_count, and error_type. The error handler records only the Python exception class, not its message or a remote response body. Do not add the CometAPI key, authentication material, environment dumps, full prompts, or raw request and response bodies to routine logs.
For a broader model-catalog procedure, use the model catalog validation guide .
Failure modes
| Symptom | Likely boundary | Safe response |
|---|---|---|
missing_environment_variable appears | The process cannot see COMETAPI_KEY | Export it in the same shell or configure the runtime’s secret store, then rerun discovery. Never print the value to diagnose visibility. |
empty_or_invalid_catalog appears | Model discovery did not produce a usable list | Treat the result as unresolved rather than guessing. Check account access, connectivity, and service state, then retry the catalog step. |
stale_or_unknown_model appears | COMETAPI_MODEL is absent from the current returned list | Copy an exact ID from the latest discovery output. Do not normalize, abbreviate, or invent an alias. |
agent_run reports an exception class | The generation or tool loop failed after selection | Preserve the sanitized stage, model ID, run ID, and exception class. Investigate locally without attaching authentication data to logs. |
required_tool_was_not_called appears | The selected model did not request the required function, or the instruction was not followed | Confirm that the selected model supports function tools and keep the prompt explicit. Do not accept a plausible ungrounded answer as proof. |
The tool returns not_found | The requested service is outside the local allowlist | Treat this as a valid tool result, not a gateway failure. Add a reviewed fixture entry only if the service should be supported. |
| The tool runs repeatedly | The model-agent loop is making unnecessary calls | Tighten the instruction and set an appropriate documented tool-call limit before connecting costly or state-changing operations. |
A production tool that writes data needs stricter controls than this fixture. Agno’s tools documentation describes confirmation requirements and tool-call limits for sensitive operations. Start with a read-only lookup, keep the exposed tool set small, and add an explicit approval boundary before introducing side effects.
FAQ
Does get_available_models() prove that a model supports tools?
No. It proves that the provider returned the model ID for the current account and request context. This tutorial separately requires a tool_call event because actual invocation is the relevant proof for the typed-function path.
Why use a typed function instead of passing an unstructured instruction?
Agno uses the function name, docstring, and type hints to construct the tool definition presented to the model. A narrow service: str input and a structured dictionary result make the contract easier to inspect than an open-ended function accepting arbitrary data.
Why not hard-code the provider’s default model?
The Agno reference documents a default, but an explicit selection makes model drift visible. Running discovery first also catches a stale tutorial value before the agent request begins.
Can I set the CometAPI base configuration explicitly?
The refetched CometAPI guide shows that explicit configuration is possible, while both provider references document a built-in default. Relying on the official provider default keeps this example smaller. If your deployment overrides it, validate that configuration separately and avoid writing authentication material into logs or source files.
What exactly counts as a successful tool test?
The catalog must be nonempty, the selected ID must be present, the local function must emit its invocation event, and the agent must complete without an exception. The final prose alone does not satisfy the test.
How should I extend the example?
Replace the fixture with one read-only application lookup, keep its arguments allowlisted and typed, and return a small serializable object. Add timeouts, bounded retries, and confirmation before exposing any operation that changes state. Re-run both the successful lookup and a controlled not_found case after each contract change.
Reader next step
Run python agent.py --list-models, choose a current tool-capable ID from the returned list, and execute the fixed billing prompt. Do not continue until the terminal shows both the tool_call and successful agent_run events. Then run the invalid-model command and retain only the sanitized event fields as operational evidence.
Before replacing the fixture with application data, review the tool-call payload boundary . If catalog selection is still unstable, complete the linked model validation workflow first rather than hard-coding an unverified ID.