Commit Graph

4085 Commits

Author SHA1 Message Date
Alem Tuzlak acc543f3c4 Merge remote-tracking branch 'origin/fix/langgraph-python-agent-config' into fix/langgraph-python-demo-runtime-bugs 2026-04-24 17:40:21 +02:00
Alem Tuzlak 2c19d30d1e Merge remote-tracking branch 'origin/fix/langgraph-python-voice' into fix/langgraph-python-demo-runtime-bugs 2026-04-24 17:40:21 +02:00
Alem Tuzlak c4a45d3d62 Merge remote-tracking branch 'origin/fix/langgraph-python-multimodal' into fix/langgraph-python-demo-runtime-bugs 2026-04-24 17:40:19 +02:00
Alem Tuzlak 623f1a2deb Merge remote-tracking branch 'origin/fix/langgraph-python-byoc-hashbrown' into fix/langgraph-python-demo-runtime-bugs 2026-04-24 17:40:18 +02:00
Alem Tuzlak c242ff3dea Merge remote-tracking branch 'origin/fix/langgraph-python-byoc-json-render' into fix/langgraph-python-demo-runtime-bugs 2026-04-24 17:39:48 +02:00
Alem Tuzlak 5f222b00ee fix(showcase/langgraph-python): emit hashbrown JSON envelope from byoc-hashbrown agent
The agent system prompt instructed the LLM to return <ui>...</ui> XML markup,
but the frontend renderer uses useJsonParser(message.content, kit.schema)
which expects a JSON object matching createUiKit(...).schema:

  {
    "ui": [
      { "metric":   { "props": { "label": "...", "value": "..." } } },
      { "pieChart": { "props": { "title": "...", "data": "[{...}]" } } },
      ...
    ]
  }

The XML syntax in useUiKit({ examples }) is hashbrowns prompt DSL for when
hashbrown drives the LLM directly (e.g. useUiChat); because this demo drives
the LLM via langgraph, the agent itself must emit the JSON wire format. With
XML content, useJsonParser returned { value: undefined } and the renderer
bailed to null, leaving the chat with only a loading indicator.

Rewrote the system prompt to produce the JSON envelope and updated the
renderer top-of-file docstring to match. Prop schemas tracked from the
current kit (metric: label+value, pieChart/barChart: title+data-as-JSON-string,
dealCard: title+stage+value, Markdown: children).
2026-04-24 17:10:29 +02:00
Alem Tuzlak 606656eeb4 fix(showcase): wire multimodal demo attachments to LangGraph agent
The multimodal demo failed three ways:

1. Try-with-sample-image rendered a broken thumbnail because the sample
   PNG on disk is a Git LFS pointer stub when LFS is not pulled at build
   time. The browser base64-encoded the 130-byte text stub, fed it into
   the chat as a valid-looking image/png, and CopilotChat rendered it
   as a broken <img>.

2. Try-with-sample-pdf attached in the composer but the agent said
   "no image/document attached". Same broken-image pipeline also hid the
   real symptom: the published @ag-ui/langgraph converter (0.0.x) only
   understands the legacy { type: binary, mimeType, data | url }
   AG-UI content-part shape. The modern { type: image | document,
   source: {...} } parts CopilotChat emits are silently filtered out of
   the LangChain message stream, so the LangGraph agent never saw them.

3. Manual drag-and-drop of an image failed for the same reason as #2.

Fixes:

- sample-attachment-buttons.tsx: reject Git LFS pointer stubs and
  validate PNG/PDF magic bytes on the client before feeding them to the
  attachment queue. Surfaces an actionable error pointing at
  git-lfs-pull instead of silently producing a broken thumbnail.

- page.tsx: install an onRunInitialized subscriber on the active agent
  that rewrites outgoing user-message image / document / audio / video
  parts to the legacy binary shape the runtime converter preserves.
  Everything else (CopilotChat UI, useAttachments upload pipeline,
  paperclip + drag-drop + paste paths) is untouched; we only retarget
  the wire format so the LangGraph side receives the attachment.

- multimodal_agent.py: after the rewrite, attachments arrive in the
  agent as LangChain image_url content parts (data URLs) regardless of
  upstream modality. Route on MIME instead of the AG-UI part type:
  image/* forwards to GPT-4o natively, application/pdf is flattened to
  text via pypdf. The modern document shape is still handled for
  forward-compat when the runtime is upgraded.

Verified: classifier helpers pass inline unit checks; tsc --noEmit
introduces no new type errors relative to HEAD (module-resolution
errors for @copilotkit/shared and lucide-react are pre-existing on this
branch and unrelated to the multimodal demo).
2026-04-24 17:01:35 +02:00
Alem Tuzlak 23dc888c3b fix(showcase): bridge forwardedProps to LangGraph configurable.properties 2026-04-24 16:03:22 +02:00
Alem Tuzlak 96a9386dce fix(showcase-shell): separate CLI init command from live demos (#4196)
## Summary

On integration profile pages (e.g. `/integrations/langgraph-python`),
the `cli-start` demo entry — which has a `command` field instead of a
`route` — was being rendered inside the Live Demos grid. Clicking it
opened `DemoDrawer` with an undefined `demoRoute`, so the iframe tried
to load `${backend_url}undefined`.

This PR:
- Splits `integration.demos` into `liveDemos` (runnable, have `route`)
and `commandDemos` (CLI-only, have `command`).
- Filters command-only entries out of the Live Demos grid.
- Adds a new **Get Started** section above Live Demos that renders each
`commandDemos` entry as a description + copy-able `<code>` block
(mirrors how `shell-dashboard` already handles these via `CommandCell`).
- Makes `Demo.route` optional and adds `Demo.command?: string` on the
`Demo` type.

Only `langgraph-python` currently has a command-only demo, so this is
the only profile page that changes visibly.

## Test plan

- [x] Ran `showcase/shell` dev server, visited
`/integrations/langgraph-python`, confirmed:
- New "Get Started" card shows `npx copilotkit@latest init --framework
langgraph-python` with a working Copy button.
- "Live Demos" grid no longer contains the broken "CLI Start Command"
tile.
- Other integrations (no `command` demos) render unchanged — Live Demos
still present, no empty "Get Started" section.
- [x] Pre-commit hooks (lint-fix, check-binaries,
test-and-check-packages — 1149 tests, commitlint) all pass.
2026-04-24 16:00:03 +02:00
Alem Tuzlak 67df4e0137 fix(showcase): invert auth demo default to authenticated + guard against 401 crash
Starting the /demos/auth cell unauthenticated meant the initial `/info`
handshake from CopilotKit returned 401 before any React error handler
could attach, crashing the page on load. Flip the default so the demo
mounts already signed in — the chat is immediately usable — and expose
Sign out / Sign in as the toggle for exercising the 401 path.

- use-demo-auth: default state flipped from unauthed to authed so the
  page mounts with a valid Bearer header.
- page.tsx: add a ChatErrorBoundary around <CopilotChat /> that catches
  any render-time throw from chat internals after signing out and shows
  a friendly in-page message instead of white-screening. The boundary
  resets on auth transitions so signing back in restores a live chat.
- auth-banner: rename Authenticate -> Sign in; status copy updated to
  Signed in / Signed out to match the inverted default.
- tests/e2e/auth.spec.ts: flip assertions — page loads authenticated,
  sign out triggers 401 on next send with banner still mounted, and
  sign in clears the error and restores successful sends.
- qa/auth.md: refreshed checklist for the authenticated-default flow.
2026-04-24 15:55:45 +02:00
Alem Tuzlak 1037e02d9d fix(showcase/langgraph-python): voice route advertises transcription + returns clear 4xx when key missing
V1 CopilotRuntime wrapper silently drops `transcriptionService`, so the
composer never saw the mic button on /voice. Write it onto the V2 runtime
instance directly, and wrap the OpenAI service with a guard that throws an
"api key missing" error when OPENAI_API_KEY is absent (mapped by the
runtime error categorizer to AUTH_FAILED / 401) instead of the opaque 503
users were hitting.
2026-04-24 15:45:04 +02:00
Alem Tuzlak 74aaa4ad84 fix(showcase): forward children through byoc-json-render MetricCard wrapper 2026-04-24 15:11:00 +02:00
Alem Tuzlak 61e5976815 fix(showcase): wrap byoc-json-render Renderer in JSONUIProvider
Clicking any suggestion in the byoc-json-render demo crashed with
"useVisibility must be used within a VisibilityProvider". Renderer from
@json-render/react relies on StateProvider, VisibilityProvider,
ValidationProvider, and ActionProvider contexts that its internal
ElementRenderer consumes, but the assistant-message slot was rendering
Renderer bare.

Wrap it in JSONUIProvider (which composes all four required providers)
so streamed spec output renders without a context crash.

Also pass children through the MetricCard catalog wrapper. The agent's
Sales Dashboard worked example uses a MetricCard as the root with a
BarChart nested in its children array; previously that chart was
silently dropped by the registry wrapper, which only rendered the
MetricCard itself.
2026-04-24 15:00:10 +02:00
Alem Tuzlak 698bf85bbb fix(showcase/langgraph-python): align byoc-hashbrown example with strict schema
Wrap PieChart + BarChart with string-accepting wrappers that JSON-parse the
data prop, and change the hashbrown schema from s.streaming.array(...) to
s.string(). Drop the non-schema example props (trend on metric, assignee
+ dueDate on dealCard).

Wave 4a was ported from the starter, which used an older hashbrown where:
  - optional props were declared via .optional()
  - array props accepted stringified JSON in example JSX attributes

@hashbrownai/core@0.5.0-beta.4 dropped .optional() (props omitted from the
schema are treated as not-present) AND tightened example-prompt validation
to reject strings against array schemas. The build failed at prerender:

  Error: Example prompt has 5 errors:
  Prop 'trend' is not defined on <metric>
  Prop 'data' on <pieChart>: Expected an array at: <root>
  Prop 'data' on <barChart>: Expected an array at: <root>
  Prop 'assignee' is not defined on <dealCard>
  Prop 'dueDate' is not defined on <dealCard>

Modeling data as a string matches how the LLM streams it anyway (JSON
tokens through useJsonParser), and the wrappers are defensive about
partial tokens — they render nothing if the parse fails mid-stream.
2026-04-24 13:20:46 +02:00
Alem Tuzlak 6f9f3f220e Merge remote-tracking branch 'origin/main' into fix/showcase-shell-cli-start-not-live-demo
# Conflicts:
#	showcase/shell/src/data/registry.json
2026-04-24 13:16:45 +02:00
Alem Tuzlak d936bc726d fix(showcase-shell): guard demo.route in viewer + preview, share Demo type
Review feedback from #4196:

- `[slug]/[demo]/page.tsx` constructed `${backend_url}${demo.route}`
  without a null check, so command-only demos (which have no `route`)
  rendered an iframe pointing at `${backend_url}undefined`. Now builds
  the src only when `demo.route` exists and renders a 'no live preview'
  panel otherwise, mirroring the Get Started section on the profile
  page. Also replaces the `any`-typed state with proper `Demo` and
  `Integration` types imported from `@/lib/registry`.
- `[slug]/[demo]/preview/page.tsx` had the same bug — already typed
  but TypeScript doesn't catch template-literal coercion of undefined.
  Now bails with a command-focused message before concatenating.
- `profile-client.tsx` no longer duplicates `Demo`/`Integration`
  interfaces — deleted the local copies and imports from
  `@/lib/registry`. copyDemoCommand's catch now logs the failure so a
  double-failure (no clipboard API + blocked prompt) is diagnosable.
  Comment above the live-demos section updated from 'Demos' to
  'Live Demos' to match the rendered heading.
2026-04-24 13:06:45 +02:00
Alem Tuzlak 015bab3054 fix(showcase/langgraph-python): lazy-init voice runtime to unblock build
The voice route was instantiating OpenAI() at module scope, which Next.js
triggers during 'collect page data' at build time. The Docker build context
does NOT have OPENAI_API_KEY set — only the running container does — so
every showcase-langgraph-python deploy has been failing since the voice
demo merged (#4224):

  Error: Missing credentials. Please pass an `apiKey`, or set the
  `OPENAI_API_KEY` environment variable.
  [Error: Failed to collect page data for /api/copilotkit-voice]

Wrap the OpenAI + TranscriptionService + CopilotRuntime construction in
a getRuntime() function that runs on first request. Cached after first
call so there's no per-request cost in production.

This unblocks the full showcase deploy pipeline, not just voice.
2026-04-24 13:03:35 +02:00
Alem Tuzlak eada8b1dbe chore: track *.wav via Git LFS and migrate sample.wav
Add *.wav to .gitattributes so audio assets follow the same LFS policy
as the existing *.png / *.pdf / *.gif / *.jpg / *.jpeg / *.mp4 / *.webm
rules. Re-stage sample.wav so the committed object becomes an LFS
pointer instead of a raw 87 KB blob.
2026-04-24 12:52:06 +02:00
Alem Tuzlak 8d14aef816 chore(showcase/langgraph-python): add sample binaries for voice + multimodal demos
- public/demo-audio/sample.wav (87KB, 16kHz mono, ~2.7s) — TTS "What is the weather in Tokyo?" for the voice demo's "Play sample" button
- public/demo-files/sample.png (10KB) — CopilotKit logo for the multimodal demo's "Try with sample image"
- public/demo-files/sample.pdf (2.4KB, one page, contains "CopilotKit" 9x) — quickstart excerpt for the multimodal demo's "Try with sample PDF"

Makes both demos fully self-contained end-to-end; the "Try with sample X" buttons now work without the user providing their own files.
2026-04-24 12:39:48 +02:00
Alem Tuzlak 4ba79746ae Merge branch 'feat/wave4b-byoc-json-render-demo' into consolidation 2026-04-24 12:28:28 +02:00
Alem Tuzlak 4ef8df8028 Merge branch 'feat/wave4a-byoc-hashbrown-demo' into consolidation 2026-04-24 12:25:02 +02:00
Alem Tuzlak 102f680bc1 Merge branch 'feat/wave3b-agent-config-demo' into consolidation 2026-04-24 12:21:51 +02:00
Alem Tuzlak 1a78be938b Merge branch 'feat/wave3a-auth-demo' into consolidation 2026-04-24 12:19:14 +02:00
Alem Tuzlak 11da7248f0 Merge branch 'feat/wave2b-multimodal-demo' into consolidation 2026-04-24 12:17:14 +02:00
Alem Tuzlak e5d2612ca9 fix(showcase): sync counts and regen langgraph-python starter after merge
- Bump generate-catalog.test counts for LGP multimodal (wired 31->32,
  unshipped 6->5; metadata wired 176->177, unshipped 486->485).
- Regenerate langgraph-python starter: add multimodal_agent.py, langgraph.json
  graph entry, and pypdf requirement.
2026-04-24 11:09:06 +02:00
Alem Tuzlak 36222515c8 fix(showcase/langgraph-python): regenerate starter + bump catalog test counts
Re-run `generate-starters.ts` for langgraph-python to emit the
byoc_json_render_agent.py starter agent and register it in langgraph.json
(post-merge drift-check was failing because the starter hadn't been
regenerated after adding the byoc-json-render demo).

Bump generate-catalog.test LGP-feature-count assertions 32->33 and
wired-totals 176->177 / 31->32, unshipped 486->485 / 6->5 to match the
new manifest.
2026-04-24 11:06:59 +02:00
Alem Tuzlak 860dfb7187 test(showcase): bump generate-catalog LGP counts for voice demo (32 wired, 1 stub, 5 unshipped) 2026-04-24 11:03:26 +02:00
Alem Tuzlak 825c5404a9 fix(showcase): regen langgraph-python starter + bump catalog counts
Regenerate langgraph-python starter so agent_config_agent is included
(drift-check fails without this). Bump generate-catalog.test.ts counts
to match wiring of agent-config: LGP wired 31→32, LGP unshipped 6→5,
total wired 176→177, total unshipped 486→485.
2026-04-24 11:03:22 +02:00
Alem Tuzlak 03ebe81863 test(showcase-scripts): bump generate-catalog counts for auth demo (Wave 3a) 2026-04-24 10:59:02 +02:00
github-actions[bot] 0d25d1f187 style: auto-fix formatting 2026-04-24 08:56:43 +00:00
Alem Tuzlak 72e94bba29 fix(showcase): regen langgraph-python starter + bump catalog test counts 31/176 -> 32/177 for byoc-hashbrown 2026-04-24 10:56:02 +02:00
github-actions[bot] 355b51c144 style: auto-fix formatting 2026-04-24 08:56:01 +00:00
Alem Tuzlak 9bbc5eb62e Merge branch 'main' into feat/wave4b-byoc-json-render-demo
Resolves conflicts:
- manifest.yaml / constraints.yaml / docs-links.json: keep both byoc-json-render
  and main's re-wired open-gen-ui + open-gen-ui-advanced entries
- scripts/hooks/check-binaries.sh: keep both shell-docs and shell-dojo
  demo-content exclusions (identical lines, different ordering)
- pnpm-lock.yaml (langgraph-python): regenerated via pnpm install
- shell/shell-dojo/shell-docs data JSONs: accept main's deletion (now
  regenerated by nx targets), then regenerate via scripts/generate-registry.ts
  and scripts/bundle-demo-content.ts

Bump generate-registry.test expected feature/demo count 32 -> 33 to account
for the new byoc-json-render demo.
2026-04-24 10:53:56 +02:00
Alem Tuzlak 7c66420a9f Merge remote-tracking branch 'origin/main' into feat/wave2b-multimodal-demo
# Conflicts:
#	showcase/aimock/feature-parity.json
#	showcase/packages/langgraph-python/docs-links.json
#	showcase/packages/langgraph-python/manifest.yaml
#	showcase/shell-docs/src/data/demo-content.json
#	showcase/shell-docs/src/data/registry.json
#	showcase/shell-dojo/src/data/demo-content.json
#	showcase/shell-dojo/src/data/registry.json
#	showcase/shell/src/data/constraints.json
#	showcase/shell/src/data/demo-content.json
#	showcase/shell/src/data/docs-status.json
#	showcase/shell/src/data/registry.json
2026-04-24 10:51:23 +02:00
github-actions[bot] e0190fd25d style: auto-fix formatting 2026-04-24 08:47:16 +00:00
github-actions[bot] d8fddba4de style: auto-fix formatting 2026-04-24 08:46:55 +00:00
github-actions[bot] ec80ac367b style: auto-fix formatting 2026-04-24 08:46:45 +00:00
Alem Tuzlak 5e3004be9c Merge remote-tracking branch 'origin/main' into feat/wave2a-voice-demo
# Conflicts:
#	showcase/shell-docs/src/data/demo-content.json
#	showcase/shell-docs/src/data/registry.json
#	showcase/shell-dojo/src/data/demo-content.json
#	showcase/shell-dojo/src/data/registry.json
#	showcase/shell/src/data/demo-content.json
#	showcase/shell/src/data/docs-status.json
#	showcase/shell/src/data/registry.json
2026-04-24 10:46:20 +02:00
Alem Tuzlak 022db41164 chore: merge origin/main into feat/wave4a-byoc-hashbrown-demo
Resolves conflicts in:
- showcase/packages/langgraph-python/manifest.yaml (byoc-hashbrown + open-gen-ui demos/features coexist)
- showcase/packages/langgraph-python/docs-links.json (subagents path from main + byoc-hashbrown entry)
- showcase/shared/constraints.yaml (merge both feature additions)
- Regenerated shell/*/data JSONs now gitignored on main; deletions accepted.

Bumps generate-registry.test.ts langgraph counts 32 -> 33 (byoc-hashbrown added).
2026-04-24 10:45:53 +02:00
Alem Tuzlak c12426b69e chore: merge origin/main into feat/wave3b-agent-config-demo
Resolves textual conflicts in manifest.yaml and docs-links.json (keeps
both open-gen-ui* from main and agent-config from this branch). Accepts
main's deletion of generated shell*/src/data/*.json (now gitignored).
Bumps langgraph-python feature/demo count test from 32 to 33.
2026-04-24 10:45:17 +02:00
Alem Tuzlak c2db5e706f chore: merge origin/main into feat/wave3a-auth-demo
Resolved conflicts:
- showcase/packages/langgraph-python/manifest.yaml: kept auth (HEAD) +
  open-gen-ui / open-gen-ui-advanced (main) in both features and demos.
- showcase/packages/langgraph-python/docs-links.json: kept auth entry
  from HEAD alongside updated subagents shell_docs_path.
- showcase/shared/constraints.yaml: kept auth + open-gen-ui* entries.
- Deleted shell/shell-dojo/shell-docs generated JSONs per main (now
  generated at build, no longer committed).

Bumped langgraph-python feature/demo count 32 -> 33 in generate-registry
test to reflect the added auth demo.
2026-04-24 10:45:05 +02:00
Jordan Ritter ec938b6675 feat(showcase-ops): widen e2e-demos probe to all integrations (#4246)
## Summary

Widens the e2e-demos probe from LangGraph Python only (Wave 1) to all 17
integrations. Every integration now gets per-demo D3 structural checks —
the probe visits each `/demos/<featureId>` route and verifies the
copilot-chat-input selector renders.

Changes:
- `namePrefix`: `"showcase-langgraph-python"` → `"showcase-"` (full
fleet)
- `max_concurrency`: 1 → 2 (halves wall time for 17 services)
- `timeout_ms`: 600000 → 900000 (headroom for ~136 routes)

No driver code changes — the e2e-demos driver is already generic. The
infra `nameExcludes` list already drops non-demo services.

## Test plan
- [ ] Next e2e-demos tick (*/6h) produces `e2e:<slug>/<featureId>` rows
for all integrations
- [ ] Dashboard Cells tab shows D3 depth for non-LGP integrations
2026-04-24 01:16:05 -07:00
Jordan Ritter fc64753c2d feat(showcase-ops): widen e2e-demos probe to all integrations
Relaxes the namePrefix filter from "showcase-langgraph-python" (Wave 1)
to "showcase-" (full fleet). All 17 integrations now get per-demo D3
structural checks — goto each /demos/<featureId> route + verify the
copilot-chat-input selector renders.

Also bumps max_concurrency from 1 to 2 (halves wall time for 17
services) and timeout from 600s to 900s (headroom for ~136 routes at
~5s each worst-case).

The infra nameExcludes list already drops non-demo services. No driver
code changes needed — the e2e-demos driver is already generic.
2026-04-24 01:14:12 -07:00
Jordan Ritter abd5759324 fix(showcase): use single PB subscription to prevent SSE connection race
Seven concurrent useLiveStatus hooks each opened their own PocketBase
SSE connection during hydration. The PB SDK multiplexes subscriptions
over a single EventSource, but the concurrent subscribe() calls raced
the initial SSE handshake — some POST /api/realtime calls fired before
the clientId was available, returning 400. After MAX_RECONNECT_ATTEMPTS
(3), affected hooks set connection="error", triggering the "dashboard
unavailable" banner on all tabs.

Fix: replace 7 per-dimension hooks with a single useLiveStatus() call
(no dimension filter). All rows are fetched in one subscription and
the existing client-side keyFor() lookups filter by dimension
implicitly. Eliminates the 7-way race, reduces PB connection count
from 7 to 1, and removes the aggregateConnection helper entirely.

Verified live at dashboard.showcase.copilotkit.ai — zero banners, all
four tabs show real PB data with D2/D4 depth chips.
2026-04-24 01:04:49 -07:00
github-actions[bot] ba4d47e45f style: auto-fix formatting 2026-04-24 07:49:44 +00:00
Jordan Ritter 692d47bb7c fix(showcase): wire build-time env vars + flatten table alignment
Three fixes batched to minimize PR churn:

1. Dockerfile: add ARG NEXT_PUBLIC_POCKETBASE_URL so the PB URL gets
   baked into the Next.js bundle at build time. Without this, pb.ts
   resolves to the sentinel URL and the dashboard shows "unavailable"
   on every tab. Pre-existing bug exposed by fresh deploys.

2. showcase_deploy.yml: pass NEXT_PUBLIC_POCKETBASE_URL and
   NEXT_PUBLIC_SHELL_URL as build args for the shell-dashboard service
   in the CI matrix. Neither was ever passed before.

3. cell-matrix.tsx + parity-matrix.tsx: flatten nested table pattern
   that caused column misalignment. Category rows used colSpan with
   an inner <table> whose columns floated independently of the header.
   Replaced with useCollapsible hook + flat sibling <tr> rows.

Also regenerates package-lock.json for the plugin-react downgrade
from PR #4241 (npm ci was failing in Docker).

Local Docker build verified with --build-arg for both NEXT_PUBLIC vars.
2026-04-24 00:48:12 -07:00
Jordan Ritter d9302a2d8d fix: prevent ag2 infinite tool-call loop and agno request timeouts (#4242)
## Summary

- **ag2**: Add `max_consecutive_auto_reply=15` to `ConversableAgent` to
prevent infinite tool-call loops that flood Railway's log stream at 500+
logs/sec and starve health probes
- **agno**: Raise `OpenAIChat` timeout from default to 120s (aimock
proxy latency) and add `tool_call_limit=15` safety net

## Root Cause

**ag2 (9/12 ticks 503)**: AG2's `ConversableAgent` with
`human_input_mode="NEVER"` has no built-in turn limit. When the LLM
enters a repetitive tool-call pattern (observed: hundreds of
`get_weather(Tokyo)` calls per second), the agent loops indefinitely.
This generates 500+ log lines/sec hitting Railway's rate limit, the
agent becomes unresponsive to health probes, the watchdog kills it after
90s, and the container restarts. Cycle repeats.

**agno (3/12 ticks 503)**: The agno `OpenAIChat` model uses httpx's
default timeout, which is too short when requests route through the
aimock proxy under load. Observed `"Error in Agent run: Request timed
out."` in logs followed by agent restart. Less frequent than ag2 because
it only happens under concurrent load.

## Test plan

- [ ] Deploy ag2 and agno via CI (push to main triggers showcase_deploy)
- [ ] Monitor health for 30 min post-deploy: `curl
showcase-ag2-production.up.railway.app/api/health`
- [ ] Verify Railway logs no longer show rate-limit messages or infinite
tool-call output
- [ ] Verify watchdog kill events drop to zero
2026-04-23 23:53:22 -07:00
Jordan Ritter 6ad352bd99 fix: regenerate ag2 and agno starters from package sources
The previous commit edited package source files but didn't regenerate
the starters, causing drift-check to fail. Running generate-starters.ts
propagates the max_consecutive_auto_reply, timeout, and tool_call_limit
changes into the starter outputs.
2026-04-23 23:48:11 -07:00
Jordan Ritter 8d7d6813e5 fix: prevent ag2 infinite tool-call loop and agno request timeouts
ag2: The ConversableAgent with human_input_mode="NEVER" has no
built-in turn limit, so when the LLM repeatedly requests the same
tool call the agent loops indefinitely.  This floods Railway's log
stream at 500+ logs/sec, makes the agent unresponsive to health
probes, and the watchdog kills it every ~90s.  Add
max_consecutive_auto_reply=15 to cap the loop.

agno: The OpenAIChat model defaults to httpx's short timeout, which
is insufficient when requests route through aimock.  Observed
"Request timed out" errors that crash the agent run.  Raise timeout
to 120s and add tool_call_limit=15 as a safety net against runaway
loops.
2026-04-23 23:43:20 -07:00
github-actions[bot] 9acb3e3f24 style: auto-fix formatting 2026-04-24 06:38:58 +00:00