API Clients + structured JSON results: app-level tokens for Synap/WSIT integration

- New api_clients + api_client_scopes tables; tokens scoped per-instance
- Admin UI tab at /admin for token create/rotate/revoke/delete with one-time reveal
- Dual-auth dependency (user session OR Bearer app token) on trigger + runs endpoints
- /api/instances/{id}/trigger pre-creates a run and returns run_id + cached last_result instantly
- New GET /api/runs/{id} for polling
- Generic trigger path for sub-agent instances (weather, calendar, etc.)
- runs.result column for structured JSON alongside markdown output
- agent_catalog.result_schema describes each agent's result shape
- Weather, daily-briefing, project-monitor retrofitted to emit structured results
- log_run: env INSTANCE_ID/RUN_ID only used when target matches, so nested sub-agents don't clobber parent runs
- Wiki docs: API Clients & Token Scoping + Calling Agents From Your Apps
This commit is contained in:
Eric Jungbauer
2026-04-20 17:54:32 +00:00
parent f01553c511
commit 043aa18f3f
8 changed files with 983 additions and 111 deletions
+35 -17
View File
@@ -283,7 +283,8 @@ def run(config, user_id=None, instance_id=None):
project_name = config.get("project_name", "Unknown Project")
if not user_id:
return f"## {project_name}\n\n*No user context for LLM.*\n", "error: no user_id"
md = f"## {project_name}\n\n*No user context for LLM.*\n"
return {"markdown": md, "summary": "error: no user_id", "result": None}
print(f" Collecting data for {project_name}...")
@@ -313,16 +314,17 @@ def run(config, user_id=None, instance_id=None):
print(f" Calling LLM for analysis...")
try:
result = llm_complete(user_id, prompt, system=SYSTEM_PROMPT, max_tokens=2000)
llm_result = llm_complete(user_id, prompt, system=SYSTEM_PROMPT, max_tokens=2000)
except RuntimeError as e:
err = str(e)
log_run(AGENT_ID, "failed", err=err, instance_id=instance_id)
return f"## {project_name}\n\n*LLM error: {err}*\n", f"error: {err}"
md = f"## {project_name}\n\n*LLM error: {err}*\n"
return {"markdown": md, "summary": f"error: {err}", "result": None}
report_md = result["text"]
model = result["model"]
tokens_in = result["input_tokens"]
tokens_out = result["output_tokens"]
report_md = llm_result["text"]
model = llm_result["model"]
tokens_in = llm_result["input_tokens"]
tokens_out = llm_result["output_tokens"]
print(f" LLM: {model}, {tokens_in}+{tokens_out} tokens")
@@ -372,15 +374,31 @@ def run(config, user_id=None, instance_id=None):
if links_md:
section += f"\n{links_md}\n"
log_run(AGENT_ID, "success", output=f"{project_name}: {summary[:100]}", instance_id=instance_id, metadata={
"project": project_name,
"model": model,
"tokens_in": tokens_in,
"tokens_out": tokens_out,
structured = {
"project_name": project_name,
"app_url": app_url or None,
"wiki_collection_id": wiki_collection or None,
"wiki_report_id": doc_id,
})
"gitea_repo": gitea_repo or None,
"summary": summary,
"report_markdown": report_md,
"model": model,
"tokens": {"input": tokens_in, "output": tokens_out},
"generated_at": datetime.now(MT).isoformat(),
}
return section, summary
log_run(AGENT_ID, "success", output=f"{project_name}: {summary[:100]}",
instance_id=instance_id,
result=structured,
metadata={
"project": project_name,
"model": model,
"tokens_in": tokens_in,
"tokens_out": tokens_out,
"wiki_report_id": doc_id,
})
return {"markdown": section, "summary": summary, "result": structured}
if __name__ == "__main__":
@@ -398,6 +416,6 @@ if __name__ == "__main__":
"wiki_collection_id": args.wiki_collection,
"gitea_repo": args.gitea_repo,
}
section, summary = run(config, user_id=args.user_id, instance_id=args.instance_id)
print(section)
print(f"\nSummary: {summary}")
out = run(config, user_id=args.user_id, instance_id=args.instance_id)
print(out["markdown"])
print(f"\nSummary: {out['summary']}")