Skip to content

sparc-service: WatsonX reasoning model patch not committed — semantic validation broken on fresh deployments #676

Description

@vz-ibm

Problem

sparc_service/providers.py in the published sparc-service:latest image is missing a required monkey-patch for WatsonX reasoning models. Every fresh deployment using make install PROVIDER=watsonx MODEL=openai/gpt-oss-120b returns decision: error on all semantic SPARC calls immediately.

Root cause

gpt-oss-120b on WatsonX is a chain-of-thought reasoning model. ALTK's WatsonxLiteLLMClientOutputVal passes the JSON schema as response_format, but the model returns its answer in reasoning_content instead of content. ALTK's _parse_llm_response only reads content and raises ValueError: No content or tool calls found in response.

Error seen on every fast_track /reflect call:

{
  "decision": "error",
  "issues": [
    {
      "issue_type": "error",
      "metric_name": "function_selection.function_selection_appropriateness",
      "explanation": "LLM execution error in function selection metric: No content or tool calls found in response"
    },
    {
      "issue_type": "error",
      "metric_name": "general.general_hallucination_check",
      "explanation": "LLM execution error in general metric: No content or tool calls found in response"
    }
  ],
  "overall_avg_score": null,
  "execution_time_ms": 12979.8
}

Fix

Monkey-patch WatsonxLiteLLMClientOutputVal.generate and generate_async at client construction time to use schema_field=None, include_schema_in_system_prompt=True — injecting the JSON schema into the system prompt instead of passing it as response_format. This is the same pattern ALTK's Ollama provider already uses.

In sparc_service/providers.py, add the helper and call it in the watsonx branch of build_llm_client:

def _patch_watsonx_for_reasoning_models(client_cls):
    import functools

    original_generate = client_cls.generate
    original_generate_async = client_cls.generate_async

    @functools.wraps(original_generate)
    def patched_generate(self, *args, **kwargs):
        kwargs.setdefault("schema_field", None)
        kwargs.setdefault("include_schema_in_system_prompt", True)
        return original_generate(self, *args, **kwargs)

    @functools.wraps(original_generate_async)
    async def patched_generate_async(self, *args, **kwargs):
        kwargs.setdefault("schema_field", None)
        kwargs.setdefault("include_schema_in_system_prompt", True)
        return await original_generate_async(self, *args, **kwargs)

    client_cls.generate = patched_generate
    client_cls.generate_async = patched_generate_async


# In build_llm_client(), replace the watsonx branch with:
if native and settings.provider == "watsonx":
    client = client_cls(
        model_name=settings.model,
        api_key=settings.wx_api_key,
        project_id=settings.wx_project_id,
        api_base=settings.wx_url,
        timeout=settings.llm_timeout_seconds,
    )
    _patch_watsonx_for_reasoning_models(client_cls)
    return client

Long-term fix (ALTK)

WatsonxLiteLLMClientOutputVal should auto-detect reasoning models and fall back to schema-in-system-prompt mode automatically. Alternatively, _parse_llm_response should check reasoning_content as a fallback when content is empty.

Impact

  • Affects every fresh cluster deployment using WatsonX provider
  • fast_track and slow_track always return decision: error
  • syntax track (static-only) is unaffected
  • Discovered during zeus VPC deployment (2026-07-16) — previously masked because the patch was applied live inside a running Mac pod but never committed to this repo

File to fix

authbridge/sparc-service/sparc_service/providers.py

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Status
New/ToDo

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions