Agents and knowledge
Ask an agent
agent = client.get_agent(42) # a published agent's id
reply = agent.ask("How many orders were cancelled last month?")
print(reply["answer"])
follow_up = agent.ask("And the month before?", session_id=reply["session_id"])
Agent.ask(message, *, session_id=None, model=None, webapp_key=None, page_key=None) sends one message and returns the platform's chat response (answer, session_id, and, depending on the agent, sql, rows, follow_ups, …). Pass the returned session_id to continue the conversation. webapp_key (with an optional page_key) grounds a SQL agent in that webapp's dashboard cards, the way the webapp's built-in assistant is grounded.
client.list_agents() returns the agent library ({pinned, enterprise, project, recent_conversations}). client.chat(message, *, agent_id=None, ...) is the same call without a handle. See Agents for building agents.
Stream a reply
for ev in agent.ask_stream("Top 5 stores by revenue this quarter?"):
if ev["event"] == "token":
print(ev["data"]["text"], end="", flush=True)
elif ev["event"] == "tool_start":
print(f"\n[{ev['data'].get('tool')}]")
elif ev["event"] == "error":
raise RuntimeError(ev["data"])
client.stream_chat(message, *, agent_id=None, session_id=None, model=None, persona=None, system=None, webapp_key=None, page_key=None, history=None, images=None, max_tokens=None, project_id=None, org_id=None) streams from the platform's chat stream endpoint; Agent.ask_stream is the agent-bound form. Both are generators: iterate them to receive the reply as it is produced.
Frames
Every item is a dict with two keys:
{"event": "token", "data": {"text": "Store 12 leads with"}}
event | data |
|---|---|
token | {"text": ...} — the next piece of the answer. |
thinking | {"text": ...} — reasoning text, when the model emits it. |
tool_start | {"tool", "label", …} — a tool call began. |
tool_result | {"tool", "summary", …} — that tool call finished. |
citation | A citation, collected into citations by collect_stream. |
artifact | An artifact attached to the answer. |
usage | Usage figures for the turn. |
error | The stream failed; the payload describes why. |
done | The turn is complete; carries stop_reason. |
The platform also sends session (first), guardrail and paused (the agent stopped for an approval or a question). Any event is passed through unchanged; heartbeat comments and frames whose data is not JSON are skipped. A frame larger than 8 MiB without an end aborts the stream with HoneyframeError (0.1.8+).
Fold a stream into one result
from honeyframeapi import collect_stream
result = collect_stream(agent.ask_stream("Summarize last week's orders"))
print(result["text"])
print(result["tool_calls"]) # [{"tool", "label", "summary"}, …]
print(result["stop_reason"], result["error"])
collect_stream(events) returns {text, thinking, tool_calls, citations, artifact, usage, stop_reason, error}. parse_sse_frames(buffer) is the low-level parser it builds on: it splits a text buffer into complete events and returns (events, rest).
Personas, history and images
persona=selects a capability persona; the platform intersects it with your own permissions, so it can only narrow what the agent may do.history=passes earlier turns as[{"role": "user" | "assistant", "content": ...}, …].images=attaches pictures for a vision-capable model, as content parts:
import base64
png = base64.b64encode(open("chart.png", "rb").read()).decode()
for ev in client.stream_chat("What does this chart show?", agent_id=42, images=[
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{png}"}},
]):
pass
Images are ignored when the selected model has no vision support.
Knowledge banks
A knowledge bank is a document corpus an agent searches. Create one, add documents, embed them, then expose it to agents as a search tool. See Knowledge Banks.
client.kb_connector_options() # {vector_stores: [...], embedding_providers: [...]}
kb = client.create_knowledge_base(
"Support procedures",
vector_connector_id=7, # where embeddings are stored
embedding_connector_id=3, # which provider computes them
chunk_size=800, chunk_overlap=100,
)
kb.upload_doc("returns-policy.pdf")
kb.embed() # embed chunks stored before a provider was wired
hits = kb.search("How long is the return window?", top_k=5)
for h in hits["results"]:
print(round(h["score"], 3), h["source"])
tool = kb.create_tool() # a vector_search agent tool bound to this bank
kb.link_agent(agent_asset_id) # an agent's or webapp's asset id
Knowledge banks belong to the client's current project (organization-level banks are visible too). Other calls:
| Method | Does |
|---|---|
kb.docs() / kb.remove_doc(doc_id) | List or remove documents (and their chunks). |
kb.sync() | Re-ingest from the bank's source folder. |
kb.clear() | Drop every document, chunk and vector; keep the bank and its links. |
kb.check_vector_store() | Check the vector store can hold this bank's embedding size. |
kb.usage() | Which tools and agents use the bank. |
kb.update(**fields) / kb.delete() | Change settings; delete (refused with 409 while something still uses it). |
LLM connections and spend
Organization admins govern which models each LLM connection may use and what it may spend.
overview = client.llm_mesh_overview() # connections, policies, month-to-date spend
client.get_llm_policy(connector_id)
client.set_llm_policy(connector_id,
allowed_models=["model-a", "model-b"],
monthly_budget_usd=500, budget_blocking=True)
client.llm_mesh_usage(days=30) # spend by connection, model and day
set_llm_policy replaces the whole policy: allowed_models=None allows every model, monthly_budget_usd=None sets no budget, budget_blocking=True stops calls once the budget is spent (otherwise it only warns), and usable_by is all or admins. A request the platform refuses because the organization's metered budget is spent raises HoneyframeBudgetExceededError (0.1.8+); see Errors and retries.
Push notifications
client.send_push("Daily briefing ready", "Revenue is up 4% on last week.",
role="manager") # or user_ids=[...]; admin only
Sends to the registered mobile devices of the given users or role and returns {sent, pruned, total, users}.
Comments on project objects
Any project member can comment on datasets, dashboards, recipes, scenarios and models; editing and deleting are limited to the author (an admin may delete any comment).
c = project.add_comment("dataset", "fact_orders", "Store 12 looks low for March.")
project.add_comment("dataset", "fact_orders", "Source extract was late.",
parent_id=c["comment_id"])
project.list_comments("dataset", "fact_orders") # root comments, each with its replies
project.comment_count("dataset", "fact_orders")
project.edit_comment(c["comment_id"], "Store 12 looks low for March (checked).")
project.delete_comment(c["comment_id"]) # leaves a tombstone so replies stay
Verified against honeyframeapi 0.1.8.