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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions