main
7 次代码提交
| 作者 | SHA1 | 备注 | 提交日期 | |
|---|---|---|---|---|
|
|
af12e7b2bb |
Coordinate provider recovery under concurrent load (#1205)
## Problem Concurrent transient failures could start independent retry, replay, continuation, and repair loops while holding provider concurrency slots. This multiplied upstream attempts and could delay or strand terminal errors under fan-out. ## Changes | Before | After | | --- | --- | | Retry paths owned separate attempt budgets. | One logical-execution session caps all upstream work at five attempts. | | Concurrent failures backed off independently. | One provider-owned recovery episode elects a single half-open probe while followers coalesce. | | Backoff occupied stream concurrency. | Concurrency is held only while an upstream operation or stream is active. | | Provider catalog calls and stream creation used separate admission paths. | Every upstream operation uses one provider-generation admission controller. | | Cancellation could leave recovery ownership or follower state unresolved. | Cancellation releases permits, transfers probe ownership, and unregisters waiting followers. | | Late in-flight failures could cross an exhausted episode boundary. | Every coalesced execution retains that generation's terminal outcome. | | Replay tests allowed loose lifecycle assertions. | Exact SSE contracts prove retries and continuations emit one unduplicated response. | | Recovery wrappers could mask final diagnostics. | Final responses and traces retain the raw provider failure and request ID. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR coordinates provider recovery and retry work under concurrent load. The main changes are: - One five-attempt budget for each logical execution. - Provider-wide recovery episodes with one elected probe. - Shared admission for streams, catalog calls, rate limits, and concurrency. - Concurrency permits held only during active upstream work. - Cancellation-safe probe ownership and preserved final diagnostics. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Reviewed the coordinated-recovery-01-before.log to understand how the exhausted generation outcome was not preserved in a late in-flight failure. - Reviewed the coordinated-recovery-02-after.log to confirm that the updated implementation preserves the exhausted generation outcome for the same focused contract set. - Validated that the provider-admission-full-current.log shows the complete requested test file passed under Python 3.14 with uv run pytest -n 0. <a href="https://app.greptile.com/trex/runs/15050270/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/admission.py | Adds shared admission, retry budgets, recovery episodes, probe election, and cancellation handling. | | src/free_claude_code/providers/openai_chat/provider.py | Moves stream creation, replay, continuation, and repair onto one admission-owned retry session. | | src/free_claude_code/providers/stream_recovery.py | Selects replay, continuation, repair, or final failure using the remaining shared attempt budget. | | src/free_claude_code/providers/failure_policy.py | Adds recovery exhaustion handling and preserves the underlying provider error for final classification. | | src/free_claude_code/providers/runtime/factory.py | Creates one admission controller per provider generation and passes it through provider factories. | </details> <details open><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant E as Execution participant A as Admission controller participant P as Provider participant F as Concurrent follower E->>A: Open attempt A->>P: Send upstream request P-->>E: Retryable failure E->>A: Open recovery episode F->>A: Request admission A-->>F: Coalesce and wait E->>A: Claim probe A->>P: Send half-open probe alt Probe succeeds P-->>E: Valid response E->>A: Close recovery episode A-->>F: Release waiter else Probe fails P-->>E: Retryable failure E->>A: Schedule next probe or finalize error end ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant E as Execution participant A as Admission controller participant P as Provider participant F as Concurrent follower E->>A: Open attempt A->>P: Send upstream request P-->>E: Retryable failure E->>A: Open recovery episode F->>A: Request admission A-->>F: Coalesce and wait E->>A: Claim probe A->>P: Send half-open probe alt Probe succeeds P-->>E: Valid response E->>A: Close recovery episode A-->>F: Release waiter else Probe fails P-->>E: Retryable failure E->>A: Schedule next probe or finalize error end ``` </a> </details> <sub>Reviews (2): Last reviewed commit: ["Harden coordinated retry lifecycle invar..."](https://github.com/alishahryar1/free-claude-code/commit/2e871c8649d148b5eb71d21f80bf870ae2d11708) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=45554917)</sub> <!-- /greptile_comment --> |
||
|
|
a092455b54 |
Add searchable model selection to the Admin UI (#1121)
## Problem Admin model routing fields required users to construct provider-prefixed model slugs. Optional tier overrides represented inheritance as an unexplained blank value. ## Changes | Before | After | | --- | --- | | Model inputs only gained suggestions after an individual provider refresh. | Model inputs load configured and discovered canonical slugs from one Admin catalog. | | Model routing looked like unrestricted text entry. | Model routing uses the browser's searchable model dropdown while retaining manual entry. | | Tier overrides displayed an empty value for fallback routing. | Tier overrides display **None** and persist it as an unset override. | | Model refresh returned provider-shaped cache internals. | Model refresh returns the same canonical catalog consumed by the Admin UI. | | Users inferred the provider/model slug format from examples. | The Admin UI and README define and present complete provider/model slugs. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR adds searchable model selection to the Admin UI. The main changes are: - Adds a canonical catalog of configured and discovered model slugs. - Adds searchable model inputs while preserving manual entry. - Represents unset tier overrides as **None**. - Reports provider-specific model refresh failures. - Reconciles cached models when provider settings change. - Updates documentation, package metadata, and tests. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. Catalog failures no longer stop the rest of the Admin UI from loading. Partial provider refreshes now produce a visible warning. Removed credential-backed providers are pruned from the shared cache, and later stale writes are rejected. No blocking issues remain in the changed code. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex ran the requested verification for the pull request checks. - The verification completed, but local artifact references were not uploaded. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/api/admin_static/admin.js | Adds searchable model fields, optional catalog hydration, refresh warnings, and None-to-unset conversion. | | src/free_claude_code/api/admin_routes.py | Adds canonical model catalog endpoints and provider refresh failure metadata. | | src/free_claude_code/providers/runtime/discovery.py | Tracks provider refresh outcomes and separates cache eligibility from discovery eligibility. | | src/free_claude_code/providers/runtime/model_cache.py | Scopes cached model metadata to currently available providers and removes stale remote entries. | | src/free_claude_code/runtime/provider_manager.py | Reconciles cache scope during runtime replacement and returns explicit refresh results. | </details> <sub>Reviews (2): Last reviewed commit: ["Fix model catalog refresh lifecycle"](https://github.com/alishahryar1/free-claude-code/commit/d16e170055f5389e538e18dece269a5f7a8c599d) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=44373795)</sub> <!-- /greptile_comment --> |
||
|
|
f3ea35a777 |
Consolidate OpenAI-compatible provider adapters (#1069)
## Problem Sixteen OpenAI-compatible providers were represented by configuration-only subclasses and one factory function each. The sole provider family also lived under a multi-transport namespace that no longer described the codebase, while provider defaults, IDs, and instance caching passed through redundant forwarding layers. ## Changes | Before | After | | --- | --- | | Sixteen provider IDs used configuration-only subclasses. | Immutable OpenAI-chat profiles configure one concrete provider while preserving each provider's request policy. | | Every provider required a dedicated factory function. | Generic profile construction is the default; only eight adapters with real state or algorithms retain factories. | | The sole provider family lived under `providers/transports/`. | `providers/openai_chat/` directly owns shared request, stream, recovery, tool, and usage behavior. | | Provider defaults and IDs passed through forwarding modules, and constructors repeated fallback resolution. | The neutral catalog resolves complete immutable provider configuration once. | | `ProviderRuntime` wrapped a pass-through provider cache. | `ProviderRuntime` directly owns lazy provider instances and cleanup. | | Production carried the duplicated adapter structure. | The final shape removes a net 873 production lines with no compatibility shim or customer-facing provider change. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR consolidates OpenAI-compatible providers behind shared profiles and runtime construction. The main changes are: - OpenAI-chat behavior moved into `providers/openai_chat`. - Configuration-only provider subclasses replaced by immutable profiles. - Provider runtime now owns lazy instance caching and cleanup directly. - Provider defaults and IDs now resolve through the neutral catalog. - Tests and smoke helpers updated for the new provider shape. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex produced a proof for a posted P1 finding and linked it to the corresponding review comment for details. - T-Rex saved contract-validation logs for provider consolidation and preserved four log files that show the test hanging boundary and the timeout exit code 124. <a href="https://app.greptile.com/trex/runs/14125428/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/runtime/config.py | Builds resolved provider configuration from catalog descriptors, including static credentials for local providers. | | src/free_claude_code/providers/openai_chat/provider.py | Creates shared OpenAI-chat clients from resolved configuration and immutable provider profiles. | | src/free_claude_code/providers/openai_chat/profiles.py | Defines declarative profiles for formerly configuration-only OpenAI-compatible adapters. | </details> <!-- greptile_failed_comments --> <h3>Comments Outside Diff (2)</h3> 1. General comment <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **NIM OpenAI-compatible 504 exhausted streaming retry path hangs instead of completing with the expected user-facing error** - **Bug** - The broader provider consolidation suite failed all parametrized `test_nim_stream_openai_5xx_exhausted_emits_user_message` cases under xdist with worker crashes. A serial isolation run narrowed this to the same OpenAI-compatible NIM exhausted 5xx streaming path: 500, 502, and 503 completed, but the 504 case did not finish before the explicit 120s timeout, producing exit code 124. This contradicts the expected streaming retry contract that exhausted transient provider failures terminate and emit the configured user-facing error. - **Cause** - The OpenAI-compatible streaming retry/exhaustion path for NIM 504 responses appears to wait indefinitely or otherwise fail to terminate after retries are exhausted. The exact code-level loop/await point was not isolated within the validation budget, but the failure is anchored to the provider streaming retry contract exercised by `tests/providers/test_openai_compat_5xx_retry.py::test_nim_stream_openai_5xx_exhausted_emits_user_message[504-temporarily unavailable]`. - **Fix** - Inspect the OpenAI-compatible/NIM streaming retry exhaustion handling for 504 responses and ensure retry limits are enforced, the async stream is closed/cancelled on exhaustion, and the provider raises/emits the same terminal user-facing error contract as the 500/502/503 cases. Add or keep a serial regression test for the 504 exhausted stream path to prevent hangs. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> 2. General comment <a href="#"><img alt="P1" src="https://greptile-static-assets.s3.amazonaws.com/badges/p1.svg?v=9" align="top"></a> **OpenAI-compatible 5xx retry suite hangs on exhausted 502 retry path** - **Bug** - The recommended non-xdist retry validation does not complete. A verbose rerun with a 60 second timeout shows the suite passes tests through the exhausted 500 case, then times out while running `test_nim_stream_openai_5xx_exhausted_emits_user_message[502-temporarily unavailable]`. This indicates the consolidated OpenAI-compatible provider retry/error path can hang for at least the 502 exhausted-error scenario, preventing reliable validation and potentially blocking callers from receiving the expected `ExecutionFailure`. - **Cause** - The OpenAI-compatible provider's exhausted 502 retry/error handling path appears not to terminate promptly under the mocked repeated `openai.InternalServerError` scenario. The exact code location was not changed during validation, but the failure is isolated to the shared OpenAI-chat 5xx retry behavior exercised by `NvidiaNimProvider.stream_response`. - **Fix** - Debug the shared OpenAI-chat retry loop/error classification for 502 responses. Ensure retries are bounded, patched `asyncio.sleep` is awaited without real backoff during tests, and exhausted 502/503/504 errors consistently raise `ExecutionFailure` with the expected temporary-unavailable message instead of continuing work indefinitely. <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> <!-- /greptile_failed_comments --> <sub>Reviews (2): Last reviewed commit: ["Prove local provider credential resoluti..."](https://github.com/alishahryar1/free-claude-code/commit/ba226d97efdae1889bea0372c7bacf268122d65c) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43580983)</sub> <!-- /greptile_comment --> |
||
|
|
e22a38b2c2 |
Canonicalize provider failure and retry ownership (#1046)
## Problem Provider SDK classification, retry policy, canonical failures, and downstream wire errors shared exception types across layers. That blurred ownership and let cleanup or provisional Responses tool failures mask the real provider diagnostic. ## Changes | Before | After | | --- | --- | | Provider failures carried Anthropic wire types and core code classified OpenAI/httpx errors. | Protocol-neutral `ExecutionFailure` values cross layers, providers classify SDK errors, and protocol packages map wire types. | | Provider adapters could author terminal wire events. | The HTTP commit boundary selects non-2xx JSON or a protocol terminal event with one ingress request ID. | | Retry policy and diagnostic handling were spread across core and provider modules. | Providers own the unchanged retry budgets while neutral core utilities own bounded credential redaction. | | Stream cleanup could replace an already-mapped provider failure. | Cleanup records safe metadata and preserves the canonical failure, status, and diagnostic. | | An incomplete Responses tool could preempt a later provider failure. | Tool-finalization errors remain provisional so canonical provider failures take precedence. | | Readiness failures reused provider exception types. | Application-owned errors represent deterministic validation and availability phases without terminal retry headers. | | Legacy exception and recovery owners remained importable. | Obsolete modules are deleted without shims, architecture rules enforce the boundaries, and package version is 3.4.21. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR canonicalizes provider failure handling across the API boundary. The main changes are: - Adds protocol-neutral execution failure values and safe diagnostics. - Moves SDK and HTTP failure classification into provider-owned policy. - Lets Messages and Responses choose their own wire error payloads. - Preserves canonical failures across stream cleanup and committed stream failures. - Makes incomplete Responses tool errors provisional until finalization. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. No files need attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran the API failure contract suite and related tests (tests/api/test\_execution\_failure\_contract.py, tests/core/test\_failure\_protocol\_mapping.py, tests/providers/test\_execution\_failure\_boundary.py, tests/providers/test\_failure\_policy.py); 48 passed in 3.36s. - Ran the streaming boundaries tests including response streams, stream recovery, and streaming errors; 70 passed in 5.36s. - Ran the OpenAI responses tests; 20 passed in 4.42s. <a href="https://app.greptile.com/trex/runs/14071383/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/providers/transports/http.py | Adds cleanup-safe stream closing that preserves established outcomes. | | src/free_claude_code/core/openai_responses/stream.py | Preserves canonical execution failures when committed Responses streams fail. | | src/free_claude_code/core/openai_responses/streaming/assembler.py | Keeps malformed tool-call errors provisional so later provider failures can win. | | src/free_claude_code/core/failures.py | Defines neutral failure kinds and exception-group lookup for execution failures. | </details> <sub>Reviews (2): Last reviewed commit: ["Preserve canonical outcomes in grouped a..."](https://github.com/alishahryar1/free-claude-code/commit/f57f21241dbe582985627ed4fb40734b2c656809) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43468297)</sub> <!-- /greptile_comment --> |
||
|
|
f8c21a48f2 |
Introduce a typed application boundary for provider execution (#1045)
## Problem The HTTP adapter owned model routing, provider execution, and runtime-facing contracts, so API handlers depended on provider implementation types. Provider preflight also discovered private request builders dynamically, obscuring the boundary that must fail before streaming begins. ## Changes | Before | After | | --- | --- | | `api/` owned model routing and shared provider execution. | `application/` owns routing and a settings-independent `ProviderExecutor`. | | API handlers accepted `BaseProvider` callbacks. | API handlers consume the narrow structural `ProviderPort`. | | `BaseProvider` discovered `_build_request_body` dynamically. | Both transport families implement explicit abstract preflight, with LM Studio composing context validation. | | Request leases, task control, and provider model metadata had adapter/provider owners. | Application-owned ports and immutable values define those cross-package contracts. | | Boundary direction was implicit. | Architecture contracts and documentation enforce the final dependency direction. | | Package version was `3.4.19`. | Package version is `3.4.20`, with the lockfile updated. | | Coverage followed the old module layout. | Deterministic boundary/preflight regressions and live Messages/Responses smokes cover the new shape. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR adds a typed application boundary for provider execution. The main changes are: - New `application` package for routing, execution, ports, and model metadata. - API handlers now call application-owned routing and provider execution. - Provider preflight is now explicit on the transport families. - Runtime API composition now uses a task-control port for `/stop`. - Import-boundary tests, smoke references, docs, version, and lockfile were updated. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. None. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran a deterministic pytest run for the provider boundary preflight, which completed with exit code 0 and 102 tests passing in 4.77 seconds. - Launched the environment presence check as part of the preflight, which completed with exit code 0 and confirmed OPENCODE\_API\_KEY=\[REDACTED\] matched. - Attempted the live provider smoke test, which completed with exit code 0 and 2 tests skipped due to incomplete smoke configuration. <a href="https://app.greptile.com/trex/runs/14067905/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/application/execution.py | Moves shared provider execution into the application layer and keeps eager preflight before token counting and streaming. | | src/free_claude_code/application/ports.py | Adds structural provider, request-runtime, and task-control protocols used across the new boundary. | | src/free_claude_code/api/routes.py | Updates route composition to use the application provider resolver and task-control stop path. | | src/free_claude_code/providers/base.py | Makes provider preflight explicit by requiring subclasses or transport bases to implement it. | | src/free_claude_code/providers/transports/openai_chat/transport.py | Adds OpenAI-chat preflight through the same request-body builder used by streaming. | | src/free_claude_code/providers/transports/anthropic_messages/transport.py | Adds native Messages preflight through the native request-body builder. | | src/free_claude_code/providers/model_listing.py | Keeps provider model-list parsing while moving `ProviderModelInfo` ownership to the application layer. | | src/free_claude_code/runtime/bootstrap.py | Passes the runtime object through the new `tasks` service slot. | | tests/contracts/test_import_boundaries.py | Extends import-boundary tests for the new application package. | </details> <details open><summary><h3>Flowchart</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% flowchart LR API[api handlers and routes] --> Routing[application.routing] API --> Executor[application.execution] API --> Ports[application.ports] Executor --> ProviderPort[ProviderPort] ProviderPort --> Preflight[preflight_stream] ProviderPort --> Stream[stream_response] Runtime[runtime bootstrap and provider manager] --> Ports Providers[providers] --> Metadata[application.model_metadata] Executor --> Core[core anthropic and trace] Routing --> Config[config settings and model refs] ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart LR API[api handlers and routes] --> Routing[application.routing] API --> Executor[application.execution] API --> Ports[application.ports] Executor --> ProviderPort[ProviderPort] ProviderPort --> Preflight[preflight_stream] ProviderPort --> Stream[stream_response] Runtime[runtime bootstrap and provider manager] --> Ports Providers[providers] --> Metadata[application.model_metadata] Executor --> Core[core anthropic and trace] Routing --> Config[config settings and model refs] ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["Introduce typed application boundary"](https://github.com/alishahryar1/free-claude-code/commit/4cdcf97231c812c6f568ca3d74af7ce759d7f2dc) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43462788)</sub> <!-- /greptile_comment --> |
||
|
|
4951983b5e |
Replace global runtime resources with explicit ownership (#1042)
## Problem Provider, messaging, and transcription resources relied on process-global state, leaving replacement, cancellation, and shutdown ownership ambiguous. Separate server lifetimes could share event-loop-bound resources or retain failed cleanup work. ## Changes | Before | After | | --- | --- | | Provider clients found limiters through global singleton and scoped registries. | Each provider instance receives and owns one explicitly constructed limiter. | | Messaging queues and voice pipelines relied on singleton or module-global state. | Each platform owns its limiter and outbox, while the application owns one injected transcriber. | | Messaging shutdown mixed ingress, active work, delivery, and SDK cleanup. | Application shutdown quiesces ingress, drains work, closes delivery, then releases transcription and providers. | | Cancelled or failed provider cleanup could be forgotten or treated as complete. | The provider manager retains shielded generation and unpublished-runtime cleanup until it succeeds. | | Discord and Telegram startup tasks could outlive or poison runtime readiness. | Platform runtimes observe long-lived tasks and retry only independently repeatable lifecycle steps. | | Constructor-captured security and diagnostic settings appeared hot-applicable. | Admin marks those settings restart-required so applied policy matches the running resource graph. | | Lifecycle races lacked direct ownership coverage. | Deterministic cancellation, retry, isolation, teardown, and live smoke contracts protect the final ownership model. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR moves runtime resources from global state into explicitly owned application objects. The main changes are: - Provider generations own their rate limiters and cleanup tasks. - Messaging platforms own their limiter, outbox, ingress, and delivery lifecycle. - Application shutdown now runs through ordered cleanup gates. - Voice transcription is injected as an owned runtime resource. - Admin config marks constructor-captured settings as restart-required. </details> <h3>Confidence Score: 4/5</h3> The shutdown path needs a bounded cleanup result before merging. Cleanup steps that hang never reach the retryable incomplete-shutdown path. ASGI shutdown can remain stuck while waiting for an external SDK, transcriber, workflow, or provider cleanup. The retry ownership model works only after cleanup returns or raises. src/free_claude_code/runtime/application.py <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - T-Rex ran the requested verification, but its local artifact references were not uploaded. - The validation run completed successfully with EXIT\_CODE: 0 and 62 tests passed in 3.91 seconds, using the command uv run pytest -vv tests/runtime/test\_application\_runtime.py tests/runtime/test\_provider\_manager.py tests/providers/test\_provider\_runtime.py. <a href="https://app.greptile.com/trex/runs/14064214/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/runtime/application.py | Refactors shutdown into ordered retryable cleanup gates, but cleanup awaitables can still block shutdown forever. | | src/free_claude_code/runtime/asgi.py | Reports incomplete runtime shutdown when `close()` returns false. | | src/free_claude_code/runtime/provider_manager.py | Adds owned provider cleanup retry state and shielded generation cleanup. | </details> <a href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22refactor%2Fruntime-owned-resources%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22refactor%2Fruntime-owned-resources%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Asrc%2Ffree_claude_code%2Fruntime%2Fapplication.py%3A59%0A**Cleanup%20Await%20Blocks%20Shutdown**%0A%0AWhen%20a%20platform%20SDK%20stop%2C%20workflow%20drain%2C%20transcriber%20close%2C%20or%20provider%20cleanup%20hangs%2C%20this%20helper%20waits%20forever%20and%20never%20returns%20%60False%60.%20ASGI%20shutdown%20stays%20stuck%20in%20%60runtime.close%28%29%60%20instead%20of%20reporting%20an%20incomplete%20shutdown%2C%20so%20the%20retained%20resource%20graph%20cannot%20be%20retried%20cleanly.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1042&platform=github"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img alt="Fix All in Codex" src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a> <sub>Reviews (2): Last reviewed commit: ["Report incomplete runtime shutdown to AS..."](https://github.com/alishahryar1/free-claude-code/commit/338b2bd179c3875b15bbd52818dd04c780e5d46d) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43454593)</sub> > Greptile also left **1 inline comment** on this PR. **Context used:** - Context used - CLAUDE.md ([source](https://app.greptile.com/alishahryar1/github/Alishahryar1/free-claude-code/-/custom-context?memory=d2fd24d8-0dec-4faf-8ee4-e085e215a2f8)) <!-- /greptile_comment --> |
||
|
|
160d63370b |
Establish single-owner runtime with stream-safe provider hot swaps (#1036)
## Problem Provider runtime ownership was split between lifecycle code and mutable FastAPI state, so Admin replacements could leak the new runtime, double-close the old runtime, or close providers still serving active streams. The API package also owned concrete process composition, obscuring subsystem boundaries. ## Changes | Before | After | | --- | --- | | FastAPI routes inspected several concrete `app.state` resources. | FastAPI receives one explicit `ApiServices` boundary and stores only `app.state.services`. | | Admin Apply persisted config and directly replaced one runtime reference. | Admin Apply validates a candidate, commits atomically, and publishes it through the single runtime owner. | | Provider replacement could close clients used by active streams. | Generation leases retain old providers until each streaming or non-streaming response finishes. | | Provider generations owned discovery state and model metadata. | `ProviderRuntimeManager` owns one application-lifetime catalog and one discovery task across replacements. | | API modules composed provider, messaging, and managed CLI resources. | `runtime.bootstrap` composes concrete subsystems and `ApplicationRuntime` owns their lifecycle. | | Admin config, server URLs, and gateway model IDs lived under the API package. | Admin config lives under `config`, server URLs live under `config`, and gateway IDs live under `core`. | | Messaging restoration and shutdown persistence were coordinated externally. | `MessagingWorkflow` owns snapshot restoration and final persistence flushing. | | Hot-swap behavior lacked a real process-level race scenario. | Deterministic ownership tests and a credential-free subprocess smoke hold provider A while new requests switch to provider B. | | Package version was `3.4.16`. | Package version is `3.4.17` with an updated lockfile. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR centralizes server runtime ownership and provider hot swaps. The main changes are: - Adds `ApplicationRuntime` and `ProviderRuntimeManager` as the process owners. - Moves FastAPI to an explicit `ApiServices` boundary. - Retains provider generations until request and stream responses finish. - Moves admin config, server URL, and gateway model ID modules to neutral package owners. - Updates admin apply to validate, persist, and publish provider-only changes through the runtime owner. - Adds runtime ownership tests and a credential-free smoke scenario. </details> <h3>Confidence Score: 5/5</h3> This looks safe to merge. No blocking issues found in the changed code. The provider lease path releases resources on normal completion, stream close, and cancellation. The admin apply path keeps restart-required changes separate from provider-only hot swaps, and repository import paths appear updated for the moved modules. No files need follow-up attention. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran the T-Rex smoke test command and confirmed it completed with exit code 0 and pytest passing. - Monitored runtime ownership activity during the run, including a provider A stream request on model-a generation 1, an admin publish to generation 2, a new request on model-b generation 2, and the completion of the original generation 1 stream. - Collected smoke result artifacts from the .smoke-results area for gateway 1, gateway 0, and main, and made them available for review. - Opened the smoke report JSON artifacts to review the summarized outcomes for each target environment. <a href="https://app.greptile.com/trex/runs/14008910/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <details open><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | src/free_claude_code/runtime/provider_manager.py | Adds provider generation ownership, request leases, replacement, discovery refresh, and shutdown cleanup. | | src/free_claude_code/runtime/application.py | Adds the process-level owner for startup, shutdown, admin operations, messaging, and session control. | | src/free_claude_code/api/routes.py | Routes now acquire provider generation leases and bind them to response lifetime. | | src/free_claude_code/api/response_streams.py | Adds response lifetime binding so retained resources release after stream completion, cancellation, or close. | | src/free_claude_code/config/admin/persistence.py | Moves admin config persistence into `config` and adds prepared validation plus atomic managed-env commits. | | src/free_claude_code/runtime/bootstrap.py | Adds the production composition root for logging, runtime owners, services, and ASGI wiring. | | src/free_claude_code/api/__init__.py | Removes package-level API re-exports as part of the HTTP adapter boundary cleanup. | </details> <details open><summary><h3>Sequence Diagram</h3></summary> <a href="#gh-light-mode-only"> ```mermaid %%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Client participant API as FastAPI Route participant Manager as ProviderRuntimeManager participant Lease as Generation Lease participant Runtime as Provider Generation participant Admin as Admin Apply Client->>API: Request /v1/messages or /v1/responses API->>Manager: acquire() Manager-->>API: lease for current generation API->>Lease: resolve_provider() Lease->>Runtime: use provider instance Runtime-->>Client: response body or stream Admin->>Manager: replace(candidate settings) Manager->>Manager: publish new generation Manager->>Manager: retire old generation Client-->>API: response completes or disconnects API->>Lease: release() Lease->>Manager: decrement active leases Manager->>Runtime: cleanup retired generation when drained ``` </a> <a href="#gh-dark-mode-only"> ```mermaid %%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Client participant API as FastAPI Route participant Manager as ProviderRuntimeManager participant Lease as Generation Lease participant Runtime as Provider Generation participant Admin as Admin Apply Client->>API: Request /v1/messages or /v1/responses API->>Manager: acquire() Manager-->>API: lease for current generation API->>Lease: resolve_provider() Lease->>Runtime: use provider instance Runtime-->>Client: response body or stream Admin->>Manager: replace(candidate settings) Manager->>Manager: publish new generation Manager->>Manager: retire old generation Client-->>API: response completes or disconnects API->>Lease: release() Lease->>Manager: decrement active leases Manager->>Runtime: cleanup retired generation when drained ``` </a> </details> <sub>Reviews (1): Last reviewed commit: ["refactor: establish single-owner applica..."](https://github.com/alishahryar1/free-claude-code/commit/92fc06aa733b7acc34ad6ea50de8b6b4ce5cfac1) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=43349002)</sub> <!-- /greptile_comment --> |