Skip to content

feat: path preservation for routing in Olla#81

Merged
thushan merged 5 commits intomainfrom
feature/preserve-path
Nov 6, 2025
Merged

feat: path preservation for routing in Olla#81
thushan merged 5 commits intomainfrom
feature/preserve-path

Conversation

@thushan
Copy link
Copy Markdown
Owner

@thushan thushan commented Nov 6, 2025

This PR addresses a few things that was raised in #80 around the use of Docker Model Runner and Olla.

  • OpenAI profile wasn't working
  • Paths failed to translate due to the path DMR creates (we mostly deal with unpathed endpoints in commercial/enterprise workloads).

This PR adds preserve_path to the endpoint config so we can map cleanly (and also support existing endpoints).

      - url: "http://localhost:12434/engines/llama.cpp/"
        name: "local-docker"
        type: "openai-compatible"
        priority: 100
        preserve_path: true
        model_url: "/models"
        health_check_url: "/"
        check_interval: 2s
        check_timeout: 1s
      - url: "http://localhost:11434"
        name: "local-ollama"
        type: "ollama"
        priority: 100
        preserve_path: true
        model_url: "/api/tags"
        health_check_url: "/"
        check_interval: 2s
        check_timeout: 1s

Summary by CodeRabbit

Release Notes

  • New Features

    • Added OpenAI-compatible inference platform profile for seamless API integration
    • Introduced path preservation setting for flexible endpoint routing and base path handling
    • Added support for newer model patterns including GPT-5 and GPT-4o variants
  • Documentation

    • Added comprehensive path preservation guide with real-world examples and troubleshooting steps
    • Enhanced configuration reference with detailed endpoint options

fix broken tests

add proxy specific path testing

move target path resolution too & update tests
yaml formatting issue (thanks vscode)
@thushan thushan added bug Something isn't working enhancement New feature or request labels Nov 6, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 6, 2025

Walkthrough

This pull request introduces a path preservation feature for proxy endpoints, enabling base URL paths to be preserved during request proxying. It adds new OpenAI-compatible profiles, extends configuration types with PreservePath fields, implements centralised URL-building logic, and refactors proxy services to use the new builder. Comprehensive documentation and tests are included.

Changes

Cohort / File(s) Summary
OpenAI-Compatible Profiles
config/profiles/openai-compatible.yaml, config/profiles/openai.yaml
New OpenAI-compatible profile created; existing openai.yaml profile name normalised, display name and description updated, new openai_compatible API flag added, and context patterns expanded for newer GPT-5 and GPT-4 model variants (e.g., gpt-5-thinking, gpt-4.1, gpt-4o).
Path Preservation Documentation
docs/content/configuration/examples.md, docs/content/configuration/reference.md, docs/content/troubleshooting/path-preservation.md
New documentation sections added explaining path preservation feature, including configuration examples for Docker model runners, path-based gateways, and microservices; reference documentation for the new preserve_path endpoint field; and troubleshooting guide with debugging steps and decision trees.
URL Builder Core Implementation
internal/adapter/proxy/common/url_builder.go, internal/adapter/proxy/common/url_builder_test.go
New centralised URL-building logic introduced with BuildTargetURL() function and helper utilities for detecting path traversal attempts; comprehensive test suite validates preserve_path behaviour across edge cases, encoding scenarios, and real-world provider configurations.
Proxy Service Refactoring – Olla
internal/adapter/proxy/olla/service.go, internal/adapter/proxy/olla/service_retry.go, internal/adapter/proxy/olla/service_preserve_path_test.go
Proxy services refactored to delegate URL construction to centralised BuildTargetURL() function; extensive test file added covering preserve_path scenarios, provider-specific configurations, and edge cases.
Proxy Service Refactoring – Sherpa
internal/adapter/proxy/sherpa/service_retry.go, internal/adapter/proxy/sherpa/service_preserve_path_test.go
Proxy retry logic updated to use centralised URL builder; comprehensive test suite added for Sherpa service path preservation with mock backend validation across diverse provider scenarios.
Configuration Type Extensions
internal/config/types.go, internal/core/domain/endpoint.go
New PreservePath boolean field added to EndpointConfig and Endpoint structs (YAML tag: preserve_path).
Repository and Discovery Updates
internal/adapter/discovery/repository.go
LoadFromConfig() method updated to propagate PreservePath from configuration to in-memory endpoint representation.
Package Maintenance
internal/integration/network_integration_test.go
Test file package declaration updated from util to integration.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ProxyService as Proxy Service<br/>(Olla/Sherpa)
    participant URLBuilder as URL Builder<br/>(common.BuildTargetURL)
    participant Endpoint as Endpoint<br/>(PreservePath flag)
    participant Backend as Backend<br/>Service

    Client->>ProxyService: HTTP Request<br/>/prefix/path?query=value
    ProxyService->>URLBuilder: BuildTargetURL(request,<br/>endpoint, proxyPrefix)
    
    alt PreservePath = true
        URLBuilder->>Endpoint: Check PreservePath flag
        Endpoint-->>URLBuilder: true
        URLBuilder->>URLBuilder: Join base path<br/>+ request path
        Note over URLBuilder: Result: base_path/path?query=value
    else PreservePath = false
        URLBuilder->>Endpoint: Check PreservePath flag
        Endpoint-->>URLBuilder: false
        URLBuilder->>URLBuilder: Strip proxy prefix,<br/>clean path,<br/>resolve against base
        Note over URLBuilder: Result: /path?query=value
    end
    
    URLBuilder-->>ProxyService: Target URL
    ProxyService->>Backend: Proxied Request
    Backend-->>ProxyService: Response
    ProxyService-->>Client: Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • URL builder logic complexity: The BuildTargetURL() function implements path traversal detection and handles both encoded and unencoded variants; review should verify correctness of containsDotDot() and containsEncodedDotDot() helper functions, particularly edge cases around percent-encoding.
  • Test coverage breadth: Multiple test files cover similar scenarios (preserve_path, edge cases, provider configs) across different proxy services; ensure tests are not redundant and sufficiently validate the feature without duplication.
  • Proxy service refactoring scope: Two proxy implementations (Olla, Sherpa) and retry logic replaced with centralised builder; verify all call sites correctly pass proxy prefix and that previous fast-path optimisations are maintained or appropriately sacrificed.
  • Configuration propagation: Ensure PreservePath field flows correctly from config types through discovery repository to endpoint domain objects without data loss.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and accurately describes the main feature being introduced—path preservation for routing in Olla. It is concise, specific, and directly reflects the primary change across the multiple file modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/preserve-path

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@thushan thushan self-assigned this Nov 6, 2025
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
docs/content/troubleshooting/path-preservation.md (1)

19-23: Consider adding language specifiers to fenced code blocks.

Several code blocks lack language identifiers, which would improve syntax highlighting and readability. Consider adding appropriate identifiers (e.g., text, plaintext, or console for output examples).

Apply these changes:

-```
+```text
 Endpoint URL: http://localhost:8080/api/v1
 Client request: POST /v1/chat/completions
 Proxied to: http://localhost:8080/v1/chat/completions
-```
+```

And similarly for the other blocks at lines 31-35, 152-155, and 298-325.

Also applies to: 31-35, 152-155, 298-325

config/profiles/openai-compatible.yaml (1)

1-127: LGTM! Comprehensive OpenAI-compatible profile.

The profile configuration is well-structured and covers all necessary aspects: routing, API paths, model patterns, context windows, and metrics extraction. The context patterns comprehensively cover GPT model families.

Minor: Consider adding a trailing newline at the end of the file to follow standard YAML conventions.

internal/adapter/proxy/common/url_builder.go (1)

38-38: Typo in comment.

"perbent-encoded" should be "percent-encoded".

Apply this diff:

-	// Non-preserve mode: guard against traversal, including perbent-encoded dot segments.
+	// Non-preserve mode: guard against traversal, including percent-encoded dot segments.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df959f0 and 43756ee.

📒 Files selected for processing (16)
  • config/profiles/openai-compatible.yaml (1 hunks)
  • config/profiles/openai.yaml (2 hunks)
  • docs/content/configuration/examples.md (1 hunks)
  • docs/content/configuration/reference.md (2 hunks)
  • docs/content/troubleshooting/path-preservation.md (1 hunks)
  • internal/adapter/discovery/repository.go (1 hunks)
  • internal/adapter/proxy/common/url_builder.go (1 hunks)
  • internal/adapter/proxy/common/url_builder_test.go (1 hunks)
  • internal/adapter/proxy/olla/service.go (1 hunks)
  • internal/adapter/proxy/olla/service_preserve_path_test.go (1 hunks)
  • internal/adapter/proxy/olla/service_retry.go (1 hunks)
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go (1 hunks)
  • internal/adapter/proxy/sherpa/service_retry.go (1 hunks)
  • internal/config/types.go (1 hunks)
  • internal/core/domain/endpoint.go (1 hunks)
  • internal/integration/network_integration_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{go,md}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Australian English in comments and documentation

Files:

  • docs/content/configuration/reference.md
  • internal/integration/network_integration_test.go
  • docs/content/configuration/examples.md
  • internal/adapter/proxy/olla/service_retry.go
  • internal/core/domain/endpoint.go
  • internal/adapter/proxy/common/url_builder.go
  • internal/adapter/discovery/repository.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • docs/content/troubleshooting/path-preservation.md
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/config/types.go
  • internal/adapter/proxy/olla/service_preserve_path_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Write comments that explain why decisions are made rather than what the code does
Code must be formatted and linted (run fmt, lint, align) to pass make ready

Files:

  • internal/integration/network_integration_test.go
  • internal/adapter/proxy/olla/service_retry.go
  • internal/core/domain/endpoint.go
  • internal/adapter/proxy/common/url_builder.go
  • internal/adapter/discovery/repository.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/config/types.go
  • internal/adapter/proxy/olla/service_preserve_path_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
internal/integration/**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Place integration tests under internal/integration as Go test files

Files:

  • internal/integration/network_integration_test.go
internal/adapter/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place infrastructure implementations (proxies, balancers, registries, translators, etc.) under internal/adapter

Files:

  • internal/adapter/proxy/olla/service_retry.go
  • internal/adapter/proxy/common/url_builder.go
  • internal/adapter/discovery/repository.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/adapter/proxy/olla/service_preserve_path_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
internal/adapter/proxy/olla/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement Olla proxy logic under internal/adapter/proxy/olla

Files:

  • internal/adapter/proxy/olla/service_retry.go
  • internal/adapter/proxy/olla/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
internal/core/**

📄 CodeRabbit inference engine (CLAUDE.md)

Keep domain logic (entities, interfaces, constants) within internal/core per Hexagonal Architecture

Files:

  • internal/core/domain/endpoint.go
internal/adapter/proxy/sherpa/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Implement Sherpa proxy logic under internal/adapter/proxy/sherpa

Files:

  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
config/profiles/*.yaml

📄 CodeRabbit inference engine (CLAUDE.md)

Store provider-specific profiles as YAML files under config/profiles/

Files:

  • config/profiles/openai-compatible.yaml
  • config/profiles/openai.yaml
🧠 Learnings (5)
📚 Learning: 2025-10-22T08:11:10.879Z
Learnt from: CR
Repo: thushan/olla PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T08:11:10.879Z
Learning: Applies to internal/integration/**/*_test.go : Place integration tests under `internal/integration` as Go test files

Applied to files:

  • internal/integration/network_integration_test.go
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
📚 Learning: 2025-10-22T08:11:10.879Z
Learnt from: CR
Repo: thushan/olla PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T08:11:10.879Z
Learning: Applies to internal/adapter/proxy/olla/**/*.go : Implement Olla proxy logic under `internal/adapter/proxy/olla`

Applied to files:

  • internal/adapter/proxy/olla/service_retry.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/olla/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
📚 Learning: 2025-10-22T08:11:10.879Z
Learnt from: CR
Repo: thushan/olla PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T08:11:10.879Z
Learning: Applies to internal/adapter/proxy/core/**/*.go : Place shared proxy components under `internal/adapter/proxy/core`

Applied to files:

  • internal/adapter/proxy/olla/service_retry.go
  • internal/adapter/proxy/common/url_builder.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
📚 Learning: 2025-10-22T08:11:10.879Z
Learnt from: CR
Repo: thushan/olla PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T08:11:10.879Z
Learning: Applies to internal/adapter/proxy/sherpa/**/*.go : Implement Sherpa proxy logic under `internal/adapter/proxy/sherpa`

Applied to files:

  • internal/adapter/proxy/olla/service_retry.go
  • internal/adapter/proxy/common/url_builder.go
  • internal/adapter/proxy/sherpa/service_retry.go
  • internal/adapter/proxy/common/url_builder_test.go
  • internal/adapter/proxy/sherpa/service_preserve_path_test.go
  • internal/adapter/proxy/olla/service.go
📚 Learning: 2025-10-22T08:11:10.879Z
Learnt from: CR
Repo: thushan/olla PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-22T08:11:10.879Z
Learning: Applies to internal/adapter/translator/**/*.go : Put API translation logic (OpenAI ↔ Provider) under `internal/adapter/translator`

Applied to files:

  • internal/adapter/proxy/common/url_builder_test.go
🧬 Code graph analysis (7)
internal/adapter/proxy/olla/service_retry.go (1)
internal/adapter/proxy/common/url_builder.go (1)
  • BuildTargetURL (20-63)
internal/adapter/proxy/common/url_builder.go (2)
internal/core/domain/endpoint.go (1)
  • Endpoint (19-40)
internal/util/request.go (1)
  • StripPrefix (74-83)
internal/adapter/proxy/sherpa/service_retry.go (1)
internal/adapter/proxy/common/url_builder.go (1)
  • BuildTargetURL (20-63)
internal/adapter/proxy/common/url_builder_test.go (2)
internal/core/domain/endpoint.go (1)
  • Endpoint (19-40)
internal/adapter/proxy/common/url_builder.go (1)
  • BuildTargetURL (20-63)
internal/adapter/proxy/olla/service_preserve_path_test.go (3)
internal/core/domain/endpoint.go (1)
  • Endpoint (19-40)
internal/adapter/proxy/olla/service.go (1)
  • Service (65-85)
internal/adapter/proxy/config/unified.go (2)
  • OllaConfig (117-124)
  • BaseProxyConfig (43-51)
internal/adapter/proxy/sherpa/service_preserve_path_test.go (6)
internal/core/domain/endpoint.go (1)
  • Endpoint (19-40)
internal/core/ports/proxy.go (1)
  • RequestStats (46-69)
internal/adapter/proxy/olla/service.go (1)
  • Service (65-85)
internal/adapter/proxy/core/base.go (2)
  • NewBaseProxyComponents (63-78)
  • BaseProxyComponents (48-60)
pkg/pool/lite_pool.go (1)
  • NewLitePool (44-68)
internal/adapter/proxy/core/retry.go (1)
  • NewRetryHandler (28-33)
internal/adapter/proxy/olla/service.go (1)
internal/adapter/proxy/common/url_builder.go (1)
  • BuildTargetURL (20-63)
🪛 LanguageTool
docs/content/troubleshooting/path-preservation.md

[duplication] ~144-~144: Possible typo: you repeated a word.
Context: ...sts fail when endpoint is behind an API gateway - Gateway returns routing errors - Direct endpoin...

(ENGLISH_WORD_REPEAT_RULE)

🪛 markdownlint-cli2 (0.18.1)
docs/content/troubleshooting/path-preservation.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


31-31: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


152-152: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


298-298: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test (1.24)
  • GitHub Check: Cross-Platform Build Validation (windows-latest)
🔇 Additional comments (16)
internal/integration/network_integration_test.go (1)

1-1: LGTM! Package declaration now aligns with directory structure.

The package name now correctly matches the internal/integration directory path, following Go conventions.

config/profiles/openai.yaml (2)

1-5: LGTM! Profile metadata clarifies intent.

The renaming from openai-compatible to openai and the updated display name/description better reflect that this is the default OpenAI profile. The comments appropriately reference the separate openai-compatible.yaml for alternative routing.

Also applies to: 11-12


52-69: LGTM! Future-proof model context patterns added.

The expanded context patterns for GPT-5, GPT-4.1, and GPT-4o families provide forward compatibility. The context window sizes appear reasonable based on the model family tiers (thinking variant at 400K, GPT-4.1 up to 1M tokens).

internal/core/domain/endpoint.go (1)

39-39: LGTM! PreservePath field added to domain model.

The new boolean field correctly extends the Endpoint entity to support path preservation behaviour. Public visibility is appropriate for a domain field that's propagated from configuration and used by proxy services.

internal/adapter/proxy/sherpa/service_retry.go (1)

59-59: LGTM! Centralised URL construction improves consistency.

Delegating target URL construction to common.BuildTargetURL eliminates code duplication and ensures path preservation behaviour is handled consistently across proxy implementations. The refactor respects the endpoint's PreservePath configuration.

internal/config/types.go (1)

110-110: LGTM! Configuration field properly defined.

The PreservePath field is correctly added with appropriate YAML tag preserve_path. The boolean type with zero-value default (false) matches the documented default behaviour, ensuring backward compatibility.

docs/content/configuration/reference.md (2)

240-240: LGTM! Feature thoroughly documented.

The preserve_path field is well-documented with clear explanation of default vs path-preserving behaviour. The use cases (Docker Model Runner, API gateways, path-based routing) provide practical guidance for when to enable this feature.

Also applies to: 256-280


287-297: LGTM! Examples demonstrate feature usage clearly.

The example configuration shows both use cases: Docker Model Runner with preserve_path: true and standard endpoint with preserve_path: false (default). The inline comments explain the reasoning, making it easy for users to understand when to use each setting.

internal/adapter/proxy/olla/service.go (1)

462-464: LGTM! Refactor delegates to shared URL builder.

The buildTargetURL method now delegates to common.BuildTargetURL, eliminating code duplication whilst maintaining encapsulation. This ensures consistent path preservation behaviour across Olla and Sherpa proxy implementations.

internal/adapter/proxy/olla/service_retry.go (1)

67-68: LGTM! Centralised URL building logic.

The refactor to use common.BuildTargetURL properly centralises the URL construction logic and respects the preserve_path configuration. The comment clearly explains the intent.

docs/content/configuration/examples.md (1)

350-446: LGTM! Comprehensive documentation for path preservation.

The examples clearly demonstrate the preserve_path feature across realistic scenarios (Docker Model Runner, API gateways, microservices). The structure aligns well with existing documentation patterns.

internal/adapter/discovery/repository.go (1)

178-178: LGTM! Proper field propagation.

The PreservePath field is correctly propagated from configuration to the endpoint instance, consistent with other field assignments.

internal/adapter/proxy/common/url_builder_test.go (1)

1-764: LGTM! Excellent test coverage.

This comprehensive test suite properly validates the URL building logic across multiple dimensions:

  • Backward compatibility with existing behavior
  • Path preservation modes
  • Edge cases (double slashes, path traversal, encoding)
  • Query string handling
  • Real-world provider configurations
  • Performance benchmarks

The tests are well-structured, use parallel execution, and cover security concerns.

internal/adapter/proxy/olla/service_preserve_path_test.go (1)

1-739: LGTM! Comprehensive service-level tests.

The test suite properly validates the Olla service's URL building behavior, complementing the lower-level url_builder tests. Good coverage of preserve_path modes, edge cases, and real-world provider scenarios.

internal/adapter/proxy/sherpa/service_preserve_path_test.go (1)

1-690: LGTM! Excellent integration tests.

The test suite validates Sherpa's path preservation through end-to-end HTTP proxying using a mock backend server. This approach effectively verifies that the URL building logic works correctly in the full proxy flow. The helper functions and mock implementations are clean and appropriate.

internal/adapter/proxy/common/url_builder.go (1)

20-87: LGTM! Well-designed URL building logic.

The implementation properly handles:

  • Path preservation mode with correct joining of base and request paths
  • Security: Guards against path traversal (including encoded variants)
  • Performance: Optimizes common cases with fast paths
  • Query parameter preservation across all modes

The helper functions are efficient and the overall logic is sound.

@thushan thushan merged commit c995509 into main Nov 6, 2025
8 checks passed
@thushan thushan deleted the feature/preserve-path branch November 6, 2025 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant