Commit Graph
6 Commits
Author SHA1 Message Date
Chris CoutinhoandClaude Opus 4.7 adcf13f082 refactor(providers): address PR #772 review round 3 — hermetic test, lazy logging, defensive-guard tests
- test_registry.py: stub `mistralai.client.Mistral` in
  `test_registry_mistral_wins_over_ollama`, mirroring the sibling
  picker test, so the test doesn't depend on the SDK accepting
  arbitrary keys.
- openai.py: convert remaining f-string `logger.info(...)` calls to
  lazy `%s` formatting, aligning with the pattern in mistral.py and
  the repo's logging convention.
- test_mistral.py: add four tests covering the defensive RuntimeError
  guards in `embed()` and `_embed_batch_request()` — empty
  response.data, single null embedding, batch null embedding, and
  count-mismatch.
- docs/configuration.md: add `AWS_ACCESS_KEY_ID` and
  `AWS_SECRET_ACCESS_KEY` rows to the env-var reference table; they
  were already mentioned in prose but missing from the table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:28:04 +02:00
Chris CoutinhoandClaude Opus 4.7 20f1770794 refactor(providers): address PR #772 review round 2 — guard, naming, docs, tests
- _retry.py: replace `assert last_error is not None` with explicit
  `if last_error is None: raise RuntimeError(...)` so the original
  rate-limit error is preserved under `python -O`.
- openai.py: drop the `_retry_factory` alias chain; rename the bound
  decorator to `_retry_429` to match the pattern in mistral.py.
- mistral.py: comment the imports so future reviewers understand why
  `from mistralai.client import …` is the canonical path on 2.x (no
  top-level `__init__.py`; no `mistralai.models` subpackage either).
- docs/configuration.md: add `OPENAI_GENERATION_MODEL` and
  `OLLAMA_GENERATION_MODEL` rows to the env-var reference table.
- test_mistral.py: add direct unit test for the `_is_rate_limit`
  predicate (429 → True, 500 → False, missing-attr → False).
- test_registry.py: stub `mistralai.client.Mistral` in the registry
  picker test, mirroring the Ollama sibling, so the test doesn't
  depend on the SDK accepting arbitrary keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 18:11:57 +02:00
Chris CoutinhoandClaude Opus 4.7 e360a7782b refactor(providers): address PR #772 review — shared retry, cleaner imports, no-op close
Addresses the Claude Code review on PR #772 plus the SonarCloud S1192 finding:

- Extract `retry_on_rate_limit` into `nextcloud_mcp_server/providers/_retry.py`
  as a parametric decorator. OpenAI and Mistral now share the same backoff
  loop; future providers can reuse it without copy-paste.
- New `tests/unit/providers/test_retry.py` covers the decorator: 429 retry +
  success, non-429 immediate re-raise, MAX_RETRIES exhaustion, default
  predicate, and unrelated exception passthrough.
- Tighten Mistral SDK import to `from mistralai.client.errors import SDKError`
  (the canonical sub-path; the reviewer's `from mistralai.models import
  SDKError` does not exist in mistralai 2.4.5).
- Replace `MistralProvider.close()`'s direct `__aexit__` call with a no-op +
  comment — the Speakeasy-generated client has no public close hook and the
  underlying httpx client is closed by GC.
- Extract the duplicated "Embedding not supported" message to a module-level
  constant (SonarCloud S1192).
- Align `Settings.get_embedding_model_name()` Bedrock check with the registry
  by also considering `bedrock_generation_model`.
- Add the `mock_mistral_client` fixture to
  `test_mistral_no_embeddings_disabled` for parity with the rest of the file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:46:32 +02:00
Chris CoutinhoandClaude Opus 4.7 3268a13d11 feat(providers): add Mistral embedding provider, route registry through dynaconf
Adds a hosted Mistral embedding option (mistral-embed, 1024-dim) alongside
the existing Bedrock / OpenAI / Ollama / Simple providers. Implementation
mirrors OpenAIProvider: lazy dimension detection with a known-models lookup,
chunked batch requests, defensive index sort, and a 429-aware retry decorator.

In the same change, ProviderRegistry switches from os.getenv to the
dynaconf-backed Settings dataclass so all five providers share a single
configuration path. config.py gains the previously-uncovered Bedrock keys,
the new Mistral keys, the missing OPENAI_GENERATION_MODEL /
OLLAMA_GENERATION_MODEL, and SIMPLE_EMBEDDING_DIMENSION.

Auto-detection priority: Bedrock → OpenAI → Mistral → Ollama → Simple.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 17:25:24 +02:00
Chris CoutinhoandClaude 208365cd3d feat: Add OpenAI provider support for embeddings and generation
Adds OpenAI provider to the unified provider architecture (ADR-015),
supporting:
- OpenAI API (api.openai.com)
- GitHub Models API (models.github.ai/inference)
- OpenAI-compatible endpoints (Fireworks, Together, etc.)

Features:
- Embedding support with text-embedding-3-small/large models
- Text generation via chat completions API
- Automatic retry with exponential backoff for rate limits
- Provider auto-detection in registry (priority after Bedrock)

Environment variables:
- OPENAI_API_KEY: API key (required)
- OPENAI_BASE_URL: Base URL override (optional)
- OPENAI_EMBEDDING_MODEL: Embedding model (default: text-embedding-3-small)
- OPENAI_GENERATION_MODEL: Generation model (default: gpt-4o-mini)

Also adds:
- Integration tests for RAG pipeline with MCP sampling
- MCP client sampling support for integration tests
- Ground truth Q&A pairs for Nextcloud User Manual

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 00:33:32 +01:00
Chris CoutinhoandClaude 5b484c9226 feat: add unified provider architecture with Amazon Bedrock support
Refactored LLM provider infrastructure to support sustainable additions of new providers with both embedding and text generation capabilities.

## Major Changes

### Unified Provider Architecture (ADR-015)
- Created `nextcloud_mcp_server/providers/` with unified Provider ABC
- Providers now support optional capabilities (embeddings and/or generation)
- Auto-detection registry with priority: Bedrock → Ollama → Simple
- Backward compatible - existing code continues to work

### New Providers
- **BedrockProvider**: Full Amazon Bedrock integration
  - Embeddings: Titan Embed, Cohere Embed models
  - Generation: Claude, Llama, Titan Text, Mistral models
  - Model-specific request/response handling
  - AWS credential chain integration
- **OllamaProvider**: Migrated with both capabilities support
- **AnthropicProvider**: Moved from test code to production providers
- **SimpleProvider**: Migrated in-memory fallback provider

### Breaking Changes
None - full backward compatibility maintained:
- `embedding.get_embedding_service()` still works
- RAG evaluation tests updated to use unified providers
- All existing tests pass (127 unit tests)

### Testing
- Added 9 comprehensive Bedrock unit tests with mocked boto3
- All existing unit tests pass
- Type checking (ty) and linting (ruff) pass
- Verified backward compatibility

### Documentation
- `docs/ADR-015-unified-provider-architecture.md`: Comprehensive ADR
- `docs/bedrock-setup.md`: AWS setup guide with IAM permissions
- `CLAUDE.md`: Updated with provider architecture section

### Dependencies
- Added `boto3>=1.35.0` to dev dependencies (optional)

## Environment Variables

### Bedrock
- `AWS_REGION`: AWS region (e.g., "us-east-1")
- `BEDROCK_EMBEDDING_MODEL`: Model ID for embeddings
- `BEDROCK_GENERATION_MODEL`: Model ID for generation
- `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`: Optional credentials

### Ollama
- `OLLAMA_BASE_URL`: API URL
- `OLLAMA_EMBEDDING_MODEL`: Embedding model (default: "nomic-embed-text")
- `OLLAMA_GENERATION_MODEL`: Generation model

## AWS Bedrock Permissions Required

Minimal IAM policy:
```json
{
  "Effect": "Allow",
  "Action": ["bedrock:InvokeModel"],
  "Resource": ["arn:aws:bedrock:*::foundation-model/*"]
}
```

See `docs/bedrock-setup.md` for detailed setup instructions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-16 11:36:58 +01:00