Skip to content

fix: set model id for custom models in llamastack config - #208

Closed
skattoju wants to merge 21 commits into
rh-ai-quickstart:devfrom
skattoju:fix/set-model-id-for-custom-models
Closed

fix: set model id for custom models in llamastack config#208
skattoju wants to merge 21 commits into
rh-ai-quickstart:devfrom
skattoju:fix/set-model-id-for-custom-models

Conversation

@skattoju

Copy link
Copy Markdown
Contributor

Summary

  • The llama-stack subchart requires registered_resources.models.model_id to be a valid string, but when deploying a custom model (not predefined in the subchart), the id field was never set by the install script, resulting in a pydantic ValidationError on LlamaStack startup.
  • The install script now always sets global.models.$LLM.id, defaulting to the model key name. An optional LLM_ID / SAFETY_ID env var allows overriding the identifier if needed.

Test plan

  • Deployed with a custom model (llama-4-scout-17b-16e-w4a16) not predefined in the subchart — LlamaStack starts successfully
  • Deploy with a predefined model (e.g. llama-3-1-8b-instruct) to verify no regression — the subchart's default id should take precedence via Helm merge

Made with Cursor

github-actions Bot and others added 21 commits November 24, 2025 22:30
# Conflicts:
#	deploy/cluster/helm/Chart.yaml
#	deploy/cluster/helm/values.yaml
# Conflicts:
#	deploy/cluster/helm/Chart.yaml
#	deploy/cluster/helm/values.yaml
# Conflicts:
#	deploy/cluster/helm/Chart.yaml
#	deploy/cluster/helm/values.yaml
# Conflicts:
#	deploy/cluster/helm/Chart.yaml
#	deploy/cluster/helm/values.yaml
Introduce a pluggable runner architecture so the chat service can
dispatch to different agent frameworks (LlamaStack, LangGraph, CrewAI)
based on the agent's runner_type.

- Add runner_type column to virtual_agents table (default: "llamastack")
- Add runner_type to Pydantic schemas (VirtualAgentBase, VirtualAgentUpdate)
- Create BaseRunner abstract class defining the streaming interface
- Extract all LlamaStack logic into LlamaStackRunner
- Rewrite ChatService as a thin dispatcher that delegates to runners
- Add Alembic migration for the new column
- Add 24 unit tests covering dispatcher, base class, model, and schemas
- Fix dev container build: replace dnf nmap-ncat with Python socket check

Co-authored-by: Cursor <cursoragent@cursor.com>
The virtual agent GET endpoint now returns runner_type in the response
body. Update the tavern integration test to expect this field.

Co-authored-by: Cursor <cursoragent@cursor.com>
…from skattoju/langgraph-runner-abstraction

feat: runner abstraction layer for multi-framework agent support
…MCP integration (rh-ai-quickstart#201)

* feat: implement LangGraph runner for multi-framework agent execution

Add a LangGraph-based runner that uses the prebuilt ReAct agent pattern
with token-level SSE streaming, MCP tool integration, and session
persistence via an in-memory checkpointer.

Changes:
- Add LangGraphRunner implementing BaseRunner with create_react_agent
- Wire langgraph runner into ChatService dispatcher
- Add LANGGRAPH_LLM_API_BASE/KEY/DEFAULT_MODEL config settings
- Fix runner_type passthrough in virtual agent create/response APIs
- Fix model name priority: agent.model_name takes precedence over default
- Add LangGraph env vars to local dev compose.yaml
- Add unit tests for LangGraphRunner
- Add langgraph, langchain-openai, langchain-mcp-adapters dependencies

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add declarative graph engine for LangGraph agents

Add support for YAML-driven multi-node graph agents alongside the
existing ReAct agent mode. Agents with a graph_config field use a
declarative pipeline (LLM, MCP tool, MCP tool map, router nodes);
agents without it continue using create_react_agent.

Changes:
- Add graph_config JSON column to VirtualAgent model + Alembic migration
- Create graph_engine.py with async node execution, MCP protocol
  handling (httpx), template rendering, and SSE event streaming
- Add dispatch logic in LangGraphRunner: graph_config -> graph engine,
  otherwise -> ReAct agent
- Add runner_type and graph_config support to agent template system
- Add vacation planner template with declarative graph config
- Add 37 unit tests for graph engine + fix existing runner tests

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: add vacation planner MCP servers and compose integration

Port three FastMCP servers from vacation-planner POC for use with the
declarative graph engine:
- travel_research_mcp (Tavily Search API) on port 7001
- hotel_mcp (SerpApi Google Hotels) on port 7002
- flight_mcp (SerpApi Google Flights) on port 7003

Each server includes Containerfile, requirements.txt, and server.py.
Updated compose.yaml with service definitions and MCP URL env vars
for the backend. Added SERPAPI_API_KEY to .env.example.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(frontend): add LangGraph UI support for graph agent streaming

- Add runner_type and graph_config fields to Agent, AgentTemplate, and
  related TypeScript types
- Add NodeStartedEvent/NodeCompletedEvent stream event types and
  GraphNodeContentItem content type for tracking graph node execution
- Add handleNodeStarted/handleNodeCompleted stream handlers with
  nodeIdToLabel utility for human-readable node names
- Wire node_started/node_completed event dispatch in useChat hook
- Add GraphProgressTracker component (PatternFly ProgressStepper) showing
  per-node execution status with spinner/checkmark indicators
- Add GraphNodeOutputSection component for labeled, expandable per-node
  output with markdown rendering
- Update chat separateContent to detect graph nodes and render structured
  output sections instead of concatenated text
- Add runner_type selector and graph_config JSON editor to agent creation
  form with validation
- Show runner_type badge on agent cards and template deploy modal

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: incremental template sync so new YAML templates are added without DB wipe

The startup template loader previously skipped loading entirely when any
suites already existed, causing newly added templates (e.g. Vacation
Planner) to never appear in the database. Now performs an incremental
sync, inserting only missing suites and templates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use inputs.destination for flight search instead of places_list_task output

The flight_research_task node was referencing {outputs.places_list_task}
for the destination, which produced garbled LLM output. Point it at
{inputs.destination} so the user-supplied destination is used directly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: improve IATA code resolution with city-airport map and strict matching

Add a well-known city-to-airport lookup map so cities like "Paris" resolve
to CDG without relying on regex or SerpApi value scanning. Restrict
_find_iata_in_candidates to only check explicit IATA keys (iata_code,
iata, code), preventing false positives like "ILE" from "Ile-de-France".
Reorder _resolve_airport_code to check the city map before regex extraction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: handle 404 in MCP session retry and stream graph via astream

Expand the MCP session-invalid retry to also cover HTTP 404 responses
containing "session" in the body, not just 400.

Rewrite run_streaming to use LangGraph's graph.astream(state,
stream_mode="updates") instead of manually iterating nodes. This
preserves the full graph topology (conditional edges, routing) while
still yielding incremental SSE events (node_started, response,
node_completed) for real-time frontend progress updates.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: extract structured input fields from user message via LLM

Add _extract_input_fields to LangGraphRunner that uses the LLM to parse
structured parameters (destination, interests, dates, etc.) from the
user's natural-language message. Extracted values override hardcoded
defaults from input_fields in the graph config, so "weekend getaway to
Paris" correctly sets destination to Paris instead of the YAML default.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(frontend): consolidate graph progress into inline status indicators

Remove the standalone GraphProgressTracker stepper component from
chat.tsx to eliminate duplicate checkmark sets. Integrate inline status
indicators (checkmark / spinner / circle) directly into each
GraphNodeOutputSection toggle, so progress and output appear together.

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat: parallel graph node execution with auto-dependency analysis

Replace the linear node chain with automatic dependency analysis that
scans template references ({outputs.node_id}, items_path, depends_on)
to build a fan-out/fan-in topology.  Independent nodes now execute
concurrently via LangGraph's StateGraph with Annotated reducers for
safe parallel state merging.

For the vacation planner, hotel_research_task and flight_research_task
now run in parallel with places_list_task, significantly reducing
end-to-end latency.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(frontend): render graph node output as markdown with GFM support

Add remark-gfm and apply PatternFly's pf-v6-c-content class to the
GraphNodeOutputSection so that headings, links, tables, lists, and
code blocks render properly instead of displaying raw markdown text.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(test): update template_startup test for incremental sync

The test_skip_when_templates_exist test now properly mocks both the
suite and template DB queries plus the YAML loader, matching the
incremental sync logic that queries suites and templates separately.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(frontend): resolve eslint/prettier lint errors

Auto-fix prettier formatting and prefix unused nodeId param with
underscore to satisfy @typescript-eslint/no-unused-vars.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use python healthchecks for MCP servers (no curl in slim image)

The python:3.11-slim base image used by MCP server containers does not
include curl, causing healthchecks to always fail. Replace curl with a
python urllib one-liner that is guaranteed to be available.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: configure web search tool and improve LlamaStack error handling

- Register builtin::websearch tool group with Tavily search provider in
  llamastack-run.yaml so the web_search tool type is recognized
- Add env_file directive in compose.yaml to inject TAVILY_API_KEY from
  project root .env into the LlamaStack container
- Handle error chunks in StreamAggregator where type is None but an
  error dict is present in the chunk payload
- Add retry logic in the stream loop to gracefully exclude tools that
  are not found on the server, preventing 400 errors from breaking the
  entire response

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: use TCP socket healthchecks for MCP servers

The streamable-http MCP transport returns 406 Not Acceptable on plain
GET requests to /mcp, causing urllib-based healthchecks to always fail.
Switch to TCP socket connection checks which reliably verify the server
is listening without depending on HTTP response codes.

Made-with: Cursor

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: dev mode agent templates and local setup

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: align integration tests with dev model llama3.2:1b

* fix: align integration tests with LangGraph template changes

- Allow extra keys (runner_type, graph_config) in template detail response
- Add tavily-search provider to expected providers list
- Drop question-mark requirement for demo questions (LangGraph
  templates use imperative prompts)

Made-with: Cursor

* fix: add graph_config to virtual agent endpoint test

The VirtualAgent response now includes a graph_config field (from
the LangGraph feature).  Add it to the expected response so the
strict key check passes.

Made-with: Cursor

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* working

* abstract stream logic to make more readbale

* refactor

* working

* working

* working

* fix: replace SQLite source compilation with pysqlite3-binary

The backend container build was failing because `sqlite-devel` is
unavailable in UBI9 default repos and compiling SQLite from source
is fragile under QEMU emulation on Apple Silicon.

Use the `pysqlite3-binary` pip package instead, which ships a
pre-compiled SQLite >= 3.45 wheel. A startup monkey-patch in
main.py swaps it in before ChromaDB (via CrewAI) imports sqlite3.

Made-with: Cursor

* fix: route CrewAI LLM through LlamaStack instead of hardcoded gpt-4o

Add OPENAI_API_KEY and OPENAI_API_URL env vars to compose so CrewAI
can initialize without errors, and replace the hardcoded gpt-4o model
with llama3.2:1b which is actually registered in LlamaStack.

Made-with: Cursor

* fix: prevent duplicate task output in CrewAI multi-agent streaming

Task content was emitted twice: once via real-time TEXT streaming and
again when _drain_task_events re-emitted the full task output on
completion. Track which tasks already received streamed content and
skip re-emission in the drain, fixing doubled hotel results and
repeated final questions.

Made-with: Cursor

* feat: add CrewAI runner config, switch to MaaS endpoints, and add CrewAI vacation planner template

- Add CREWAI_LLM_API_BASE, CREWAI_LLM_API_KEY, CREWAI_DEFAULT_MODEL settings
  in backend config, mirroring the LangGraph runner pattern
- Switch both LangGraph and CrewAI runners from local LlamaStack OpenAI-compat
  endpoint to configurable MaaS (Model-as-a-Service) endpoints with env var
  overrides and sensible defaults
- Pass root .env file to all podman compose commands via --env-file so
  secrets and config flow through consistently
- Add env_file directive to backend service in compose.yaml
- Add CrewAI multi-agent vacation planner template with destination, hotel,
  flight researchers and itinerary planner using MCP tool servers
- Strip transient reasoning content items from assistant messages on stream
  completion in the frontend useChat hook
- Add LangGraph & CrewAI feature plan documentation
- Remove unused vulture_whitelist.py

Made-with: Cursor

* feat: add env-configured default model option to model dropdown

When LANGGRAPH_DEFAULT_MODEL or CREWAI_DEFAULT_MODEL env vars are set,
a "Default Model (Environment Configured)" option appears in the model
selector so MaaS models work without needing to be registered in
LlamaStack. Each runner resolves the sentinel to its own env-configured
default. The /llms endpoint also gracefully returns this entry when
LlamaStack is unavailable.

Made-with: Cursor

* fix: resolve lint errors in frontend and backend

Fix ESLint/Prettier formatting issues in useChat.ts and agents.tsx.
Align .flake8 max-line-length (120) with pre-commit and CI config.

Made-with: Cursor

* fix: add noqa E402 to imports in main.py for pysqlite3 swap

The pysqlite3/sqlite3 module swap must run before any other imports,
which causes flake8 to flag all subsequent imports as E402.

Made-with: Cursor

* fix: CrewAI crew stops early — flight tool unused, noisy output, bad formatting

- Remove async_execution from hotel/flight tasks for predictable sequential flow
- Simplify flight agent prompt (overly prescriptive params confused the model)
- Set max_iter=2 for tool-less agents to avoid 15 wasted ReAct cycles
- Import and use Process enum instead of raw string
- Track per-task streamed char count so fallback fires for low-output tasks
- Improve hotel/flight tool output to return clean markdown
- Strengthen itinerary planner prompt to prevent Thought/Action noise
- Add agent/task assignment logging for easier debugging

Made-with: Cursor

* fix: pass dict instead of set for streamed_tasks in test

The test_skips_reemit_for_streamed_tasks test was passing a set for
streamed_tasks, but _drain_task_events expects a Dict[str, int] and
calls .get() on it, causing an AttributeError.

Made-with: Cursor

---------

Co-authored-by: Ryan Johnson <johnson2500@live.com>
The llama-stack subchart requires each enabled model to have an `id`
field for `registered_resources.models.model_id`. When using a custom
model not predefined in the subchart, this field was never set, causing
a pydantic ValidationError on startup. Default the id to the model key
name, with an optional LLM_ID/SAFETY_ID override.

Made-with: Cursor
@skattoju
skattoju requested a review from olavtar as a code owner March 11, 2026 19:53
@ganeshmurthy

Copy link
Copy Markdown
Collaborator

Closing this PR in favor of new PR - #223
The new PR is rebased to the main branch and has been tested on an Openshift cluster

@sauagarwa sauagarwa closed this Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants