Skip to content

fix(eos_ai): LLMClient URL normalization, env var support, and unit test suite - #127

Open
iamsayanmandal wants to merge 1 commit into
embeddedos-org:masterfrom
iamsayanmandal:fix/llm-client-url-normalization-and-tests
Open

fix(eos_ai): LLMClient URL normalization, env var support, and unit test suite#127
iamsayanmandal wants to merge 1 commit into
embeddedos-org:masterfrom
iamsayanmandal:fix/llm-client-url-normalization-and-tests

Conversation

@iamsayanmandal

@iamsayanmandal iamsayanmandal commented Sep 10, 2026

Copy link
Copy Markdown

Summary

Three related fixes that make the AI/LLM subsystem production-ready and restore a green CI gate on master.

1. LLMClient URL bugs_call_openai_compat appended /v1/chat/completions unconditionally, causing /v1/v1/chat/completions when OPENAI_BASE_URL already contains /v1. Bare Ollama hosts without http:// crashed urllib. Empty api_key sent a stray Authorization: Bearer header that local servers (vLLM, llama.cpp) reject. Empty choices list returned success=True silently.

2. LLMClient has zero unit tests — added the first-ever test suite (35 tests, fully offline with mocks).

3. CI lint gate brokenruff check . failed on 4 findings before running any test. Also added missing PackageRecipe.to_dict() which caused ebuild update-index to crash.

Type of Change

  • \feat — New feature
  • \fix — Bug fix
  • docs — Documentation only
  • style — Formatting, no code change
  • \refactor — Code restructuring without behavior change
  • \test — Add or fix tests
  • \build — Build system or dependency changes
  • ci — CI/CD pipeline changes
  • perf — Performance improvement

Changes

  • Add _normalize_openai_url(): strips trailing slash, detects existing /v1, appends correct suffix — prevents /v1/v1/chat/completions double-path
  • Add _ensure_scheme(): prepends http:// to bare Ollama hosts like 192.168.1.50:11434
  • Add _build_headers(): omits Authorization header when api_key is empty
  • Add _parse_openai_response(): returns success=False with clear message when choices is empty
  • Honour OLLAMA_HOST, OLLAMA_MODEL, OPENAI_BASE_URL, OPENAI_MODEL, EOS_LLM_* env vars
  • Add tests/unit/test_llm_client.py — 35 tests across 9 test classes, zero network calls
  • Add PackageRecipe.to_dict() using external YAML key names (package, build) for round-trip correctness
  • Fix F811 duplicate import shutil in test_build_dir_resolution.py
  • Fix E402 mid-file imports in test_ci_gate.py
  • Fix W292 missing newline in test_package_recipe.py
  • Fix wrong # type: ignore[attr-defined][arg-type] in plugins/__init__.py

Testing

  • Unit tests pass (ctest --test-dir build --output-on-failure)
  • Integration tests pass
  • Manual testing performed
  • New tests added for new functionality
ruff check .      →  All checks passed! ✅
pytest tests/ -q  →  715 passed, 1 skipped, 0 failed ✅
                     (master was: 669 passed, 10 failed)

Pre-Submission Checklist

  • Code compiles without warnings (-Wall -Wextra -Werror for C)
  • All existing tests pass
  • New tests added for new functionality
  • Documentation updated if API changed
  • Commit messages follow <type>(<scope>): <description> convention
  • Branch is rebased on latest master

Related Issues

Addresses the same LLM URL issues noted in PR #120 (never merged).
Addresses the same lint gate issue noted in PR #122 (never merged).
Addresses the same to_dict() gap as PRs #119 and #124 (never merged).
Consolidated as a single coherent fix to keep review surface small.

Screenshots / Logs

$ ruff check .
All checks passed!

$ pytest tests/ -q
715 passed, 1 skipped in 13.08s

Additional Notes

All 35 new test_llm_client tests run fully offline — no Ollama, no API keys needed in CI.

…h header

- Add _normalize_openai_url() helper: prevents /v1/v1/chat/completions
  when base_url already contains /v1 (e.g. from OPENAI_BASE_URL env var)
- Add _ensure_scheme() helper: prepends http:// to bare Ollama hosts
  like '192.168.1.50:11434' so urllib does not choke
- Add _build_headers() method: omits Authorization header when api_key
  is empty, fixing compatibility with local servers (vLLM, llama.cpp)
  that reject a stray 'Bearer ' header
- Add _parse_openai_response() method: returns success=False with a
  clear error message when choices is empty, instead of silently
  succeeding with empty text
- Honour OLLAMA_HOST, OLLAMA_MODEL, OPENAI_BASE_URL, OPENAI_MODEL,
  EOS_LLM_URL, EOS_LLM_API_KEY, EOS_LLM_MODEL environment variables
  so the client is configurable without source changes
- LLMClient.auto() picks up OLLAMA_MODEL and OPENAI_MODEL from env
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.

1 participant