Skip to content

feat(jobs): first-class /v1/files + /v1/batches + /v1/fine_tuning surface (openai-family + azure) with batch cost attribution - #724

Merged
jarvis9443 merged 1 commit into
mainfrom
feat/720-batch-files-finetuning
Jul 6, 2026
Merged

feat(jobs): first-class /v1/files + /v1/batches + /v1/fine_tuning surface (openai-family + azure) with batch cost attribution#724
jarvis9443 merged 1 commit into
mainfrom
feat/720-batch-files-finetuning

Conversation

@jarvis9443

Copy link
Copy Markdown
Contributor

Summary

First-class /v1/files + /v1/batches + /v1/fine_tuning/jobs on the data plane (matrix row "Batch/Files/Fine-tuning 透传" in AISIX-Cloud#873 §⑤). Until now these provider APIs were only reachable through the raw /passthrough/:provider/* tunnel — single-provider, opaque, zero token/cost attribution.

Fixes #720

Routes

Family Routes
Files POST/GET /v1/files, GET/DELETE /v1/files/{id}, GET /v1/files/{id}/content
Batches POST/GET /v1/batches, GET /v1/batches/{id}, POST /v1/batches/{id}/cancel
Fine-tuning POST/GET /v1/fine_tuning/jobs, GET /v1/fine_tuning/jobs/{id}, POST …/cancel

Provider coverage v1: adapter openai (any OpenAI-compatible api_base) + azure-openai (resource-scoped /openai/* + api-key header). Vertex (GCS-staged) / Bedrock (S3-staged) / Anthropic native /v1/messages/batches are different wire+storage flows — deliberately out of scope here, rejected with a clear 400.

Routing: gateway-encoded resource ids (LiteLLM baseline)

The core problem: a batch create references a file id — no model in the body. Following LiteLLM's file-id encoding (litellm/proxy/openai_files_endpoints/common_utils.py), an upload names its routing Model once (multipart model field, ?model=, or x-aisix-model header) and the returned id is re-encoded as aisix-<base64url("<raw>;model,<name>")>. Every later reference (batch create input_file_id, retrieve/cancel ids, fine-tuning training_file) decodes and routes automatically. Precedence mirrors LiteLLM's scenario order: id-embedded → explicit body/query/header → first accessible OpenAI-compatible model. Raw provider ids keep working (fall back to explicit/default routing). Decoded ids are charset-guarded before touching the upstream URL path.

One deliberate extension over LiteLLM: fine-tuning job ids are also encoded (LiteLLM routes FT retrieve/cancel via config-side custom_llm_provider instead — we have no equivalent config object, and the id mechanism is already client-visible on files/batches there).

Batch usage/cost attribution (LiteLLM _handle_completed_batch parity)

On the first retrieve observing status=completed, the DP downloads the output JSONL, aggregates per-line usage grouped by the provider-billed model, and emits UsageEvent(s) with real token counts (inbound_protocol="batch", cached_prompt_tokens included). Dedup: process-local guard + deterministic request_id = "batch-<id>" so re-emission after a DP restart stays idempotent CP-side. Failed downloads release the guard and retry on the next retrieve. Management calls emit zero-token events (same contract as /passthrough, #699).

Governance envelope

Caller-key auth + model ACL, per-model IP allowlist (#697), budget + rate-limit reservation after the input guardrail (passthrough parity), input/output guardrail whole-body scan (#911 [6]), model E2E timeout, cooldown accounting on transport failures (#701), bounded metric labels (#451).

Tests

  • 11 unit tests (id codec/injection guard, form-field strip + id encode, cross-provider routing via encoded id, completed-batch attribution incl. once-only dedup, FT routing, Azure wire shape, ACL/unsupported-adapter/no-default guards).
  • DP standalone e2e (batch-files-finetuning-e2e.test.ts, real binary + etcd + mock provider, driven through the official OpenAI Node SDK): upload→batch→retrieve→content→cancel lifecycle, attribution download observed on the mock, FT lifecycle, raw-multipart routing variant, 401 baseline.

No new CP config surface (reuses existing Model/ProviderKey resources). Docs PR to api7/docs follows separately.

…face

Adds the OpenAI-compatible Files / Batches / Fine-tuning routes as
typed handlers (previously only reachable via the opaque
/passthrough/:provider/* tunnel), for openai-family and Azure OpenAI
providers.

Routing follows the LiteLLM file-id encoding baseline: an upload names
its routing Model once (multipart field / query / x-aisix-model
header); the returned id embeds the Model (aisix-<base64url>) so every
later call that references it routes automatically. Explicit
model/query/header hints and raw provider ids keep working.

Batch cost attribution (LiteLLM _handle_completed_batch parity): the
first retrieve that observes status=completed downloads the output
JSONL, aggregates per-line usage grouped by the provider-billed model,
and emits UsageEvents with a deterministic request_id (batch-<id>) for
cp-side idempotency. Management calls emit zero-token events like
/passthrough (#699).

The full auth envelope applies: caller-key auth + model ACL, IP
allowlist, budget + rate-limit reservation, input/output guardrail
blob scan (#911 [6] passthrough precedent), model E2E timeout, and
cooldown accounting on transport failures.

Fixes #720
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6008b623-c5e4-4dfa-8e88-f20a4c638075

📥 Commits

Reviewing files that changed from the base of the PR and between a09043e and 89c29d6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • CLAUDE.md
  • crates/aisix-proxy/Cargo.toml
  • crates/aisix-proxy/src/jobs.rs
  • crates/aisix-proxy/src/lib.rs
  • crates/aisix-proxy/src/state.rs
  • tests/e2e/src/cases/batch-files-finetuning-e2e.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/720-batch-files-finetuning

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

@jarvis9443
jarvis9443 merged commit b15093e into main Jul 6, 2026
10 checks passed
@jarvis9443
jarvis9443 deleted the feat/720-batch-files-finetuning branch July 6, 2026 09:20
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.

feat: first-class /v1/files + /v1/batches + /v1/fine_tuning endpoints (openai-family + azure) with batch cost attribution

1 participant