Skip to content

[MISC] Fix adapter-ops skill cost-tracking guidance and stale paths#2163

Open
chandrasekharan-zipstack wants to merge 1 commit into
mainfrom
misc/adapter-ops-cost-guidance
Open

[MISC] Fix adapter-ops skill cost-tracking guidance and stale paths#2163
chandrasekharan-zipstack wants to merge 1 commit into
mainfrom
misc/adapter-ops-cost-guidance

Conversation

@chandrasekharan-zipstack

Copy link
Copy Markdown
Contributor

What

Fixes the adapter-ops skill, which gave adapter authors incorrect guidance about cost tracking and shipped broken script paths.

Why

The cost guidance was wrong

The skill said, in three places, that get_provider() drives cost calculation, and pointed at platform-service/src/unstract/platform_service/helper/cost_calculation.py as the enforcement site.

That file no longer exists. provider is now just a metadata column on the usage row (sdk1/audit.py), never consulted for pricing.

Cost is actually looked up from the prefixed model string that validate_model() emits:

Path Site
LLM sdk1/audit.pycost_per_token(model=model_name)
Embedding sdk1/usage_handler.pylitellm.cost_per_token(...)

Both call sites catch every exception and fall back to 0.0. A miss is silent — no test fails, no build breaks. The only symptom is revenue-affecting: zero-cost usage rows.

This gap has already bitten us

PR #2156 (MiniMax LLM adapter) follows the skill's documented check correctlyget_provider() returns "minimax", which matches LiteLLM's litellm_provider exactly — and still bills $0. It subclasses OpenAICompatibleLLMParameters, whose validate_model() unconditionally prepends custom_openai/, and no LiteLLM cost-map key uses that prefix:

cost_per_token("custom_openai/MiniMax-M3") -> raises  => cost = 0.0
cost_per_token("minimax/MiniMax-M3")       -> $0.30 / $1.20 per 1M tokens

A correct get_provider() does not, on its own, guarantee cost resolution. The skill never said so.

What changed

  • Reframed the mandatory check from "verify the provider name" to "verify the model prefix resolves in LiteLLM's cost map", with the real call sites named.

  • Added a base-class decision table. If LiteLLM has a native provider for the vendor → BaseChatCompletionParameters, emit {provider}/{model} (follow OpenRouterLLMParameters). If LiteLLM prices nothing for the vendor → OpenAICompatibleLLMParameters, pin api_base (follow NvidiaBuildLLMParameters).

    NvidiaBuildLLMParameters is only a safe template because LiteLLM prices zero nvidia_nim/ chat models (3 entries, all rerankers, all $0.0) — there is no cost to forfeit. Copying its shape for a vendor LiteLLM does price is how Add MiniMax LLM adapter #2156 went wrong.

  • Swapped curl of upstream model_prices_and_context_window.json for a snippet that queries the pinned LiteLLM in the sdk1 venv — the version that actually runs.

  • Re-scoped get_provider() to what it genuinely controls: the static schema path (static/{get_provider()}.json, case-sensitive) and the usage row's provider column.

  • Repointed every documented script path from the skill's old directory name (unstract-adapter-extension) to adapter-ops. Every copy-pasteable command in SKILL.md was broken.

Checks

The verification snippet now documented in the skill was run as written and reproduces the bug it exists to catch:

$ cd unstract/sdk1 && uv run python -c "..."
[<LlmProviders.MINIMAX: 'minimax'>]
['minimax/speech-02-hd', 'minimax/speech-02-turbo', 'minimax/speech-2.6-hd']
minimax/MiniMax-M3 (0.3, 1.2)
custom_openai/MiniMax-M3 RAISES => cost silently recorded as 0.0

Docs-only change to .claude/skills/ — no runtime code touched. Pre-commit hooks pass (ruff, ruff-format, markdownlint, secret scan).

🤖 Generated with Claude Code

https://claude.ai/code/session_015i26iF1GS3x55XHEjStUeP

The skill told adapter authors that `get_provider()` drives cost
calculation and pointed at
`platform-service/.../helper/cost_calculation.py` as the enforcement
site. That file no longer exists, and `provider` is now only a metadata
column on the usage row.

Cost is actually looked up from the prefixed model string that
`validate_model()` emits, via `litellm.cost_per_token()` in
`sdk1/audit.py` (LLM) and `sdk1/usage_handler.py` (embedding). Both call
sites swallow the exception and fall back to $0, so a miss is silent.

This gap let a branded OpenAI-compatible adapter pass the documented
check — returning a `get_provider()` that matches `litellm_provider`
exactly — while still billing $0, because `OpenAICompatibleLLMParameters`
prepends `custom_openai/` and no cost-map key uses that prefix.

Rewrite the guidance to verify the model prefix instead, add a base-class
decision table (native LiteLLM provider -> BaseChatCompletionParameters;
no priced models -> OpenAICompatibleLLMParameters), and swap the curl of
upstream JSON for a snippet that queries the pinned LiteLLM in the sdk1
venv.

Also repoint every documented script path from the skill's old directory
name (`unstract-adapter-extension`) to `adapter-ops`; all copy-pasteable
commands in SKILL.md were broken.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015i26iF1GS3x55XHEjStUeP
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • Documentation
    • Updated adapter operations guidance to use the current command paths.
    • Added clearer validation guidance for model prefixes and LiteLLM pricing resolution.
    • Documented how incorrect model prefixes can result in missing cost data.
    • Expanded guidance for OpenAI-compatible and branded adapters.
    • Corrected repository path comments in adapter initialization scripts.

Walkthrough

Updated adapter-ops command documentation and repository-root references, and replaced provider-name validation guidance with prefixed model verification focused on LiteLLM cost tracking and OpenAI-compatible adapter prefixes.

Changes

Adapter operations guidance

Layer / File(s) Summary
Adapter-ops command paths and repository mapping
.claude/skills/adapter-ops/SKILL.md, .claude/skills/adapter-ops/scripts/*
Commands now reference the adapter-ops scripts directory, while initializer repository-root comments and assignment reflect the current directory structure.
Prefixed model cost verification
.claude/skills/adapter-ops/SKILL.md, .claude/skills/adapter-ops/references/*
Documentation now describes LiteLLM lookup using validated prefixed model names, $0 fallback on lookup failure, verification in the pinned environment, and base-class selection for OpenAI-compatible adapters.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it omits required template sections like How, breakage impact, migrations, env config, and related issues. Add the missing sections, especially the breakage-impact answer, plus How, migrations, env config, related issues, dependencies, testing, screenshots, and checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly reflects the main changes: adapter-ops cost-tracking guidance and stale script paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch misc/adapter-ops-cost-guidance

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.

@sonarqubecloud

Copy link
Copy Markdown

@chandrasekharan-zipstack chandrasekharan-zipstack marked this pull request as ready for review July 10, 2026 15:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.claude/skills/adapter-ops/SKILL.md (1)

193-197: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the accepted management action name.

manage_models.py accepts add-enum, not add; this documented command exits with an invalid-choice error.

-     --action add \
+     --action add-enum \
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/adapter-ops/SKILL.md around lines 193 - 197, Update the
documented manage_models.py example to use the accepted action name add-enum
instead of add, preserving the existing adapter, provider, and models arguments.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/adapter-ops/references/provider_capabilities.md:
- Around line 151-165: Remove the instructions directing readers to curl
LiteLLM’s upstream main pricing JSON near the pricing verification guidance.
Replace them with instructions to validate model-prefix pricing against the
pinned sdk1 environment and its installed LiteLLM data, keeping the guidance
consistent with production dependencies.

In @.claude/skills/adapter-ops/SKILL.md:
- Around line 241-251: Update check_adapter_updates.py so its --json mode emits
only the json.dumps(...) payload on stdout; route the “SDK1 Adapters Path”
diagnostic to stderr or suppress it when JSON output is enabled, while
preserving the diagnostic for normal output.
- Line 359: Insert a blank line between the “Common provider names” label and
the table beginning with “Display Name” to satisfy markdownlint MD058.

---

Outside diff comments:
In @.claude/skills/adapter-ops/SKILL.md:
- Around line 193-197: Update the documented manage_models.py example to use the
accepted action name add-enum instead of add, preserving the existing adapter,
provider, and models arguments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8c70cb48-b48e-46d1-b0cd-087c997c4613

📥 Commits

Reviewing files that changed from the base of the PR and between 09b6834 and 93abe9d.

📒 Files selected for processing (5)
  • .claude/skills/adapter-ops/SKILL.md
  • .claude/skills/adapter-ops/references/adapter_patterns.md
  • .claude/skills/adapter-ops/references/provider_capabilities.md
  • .claude/skills/adapter-ops/scripts/init_embedding_adapter.py
  • .claude/skills/adapter-ops/scripts/init_llm_adapter.py

Comment on lines +151 to +165
1. `LLM._record_usage()` logs tokens — no cost math.
2. `Audit.push_usage_data()` (LLM) / `UsageHandler` (embedding) calls
`litellm.cost_per_token(model=model_name)`, where `model_name` is the **prefixed** model
string emitted by `validate_model()`.
3. No cost-map entry for that string → the call raises → the bare `except` records $0.

The `provider` value from `get_provider()` rides along into the usage row's `provider` column
but is **not** used for pricing.

**Example bug**: a branded OpenAI-compatible adapter emits `custom_openai/MiniMax-M3`. LiteLLM
prices `minimax/MiniMax-M3` but has no `custom_openai/` keys, so cost silently resolves to $0 —
even though `get_provider()` correctly returns `"minimax"`.

See `references/adapter_patterns.md` → *Model Prefix Verification* for how to choose a base
class so the prefix resolves.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the stale upstream pricing verification.

This file still directs readers to curl LiteLLM’s main pricing JSON at Lines [143-145], while the new guidance requires checking the pinned sdk1 environment. That can validate a prefix against data different from the version used in production.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/adapter-ops/references/provider_capabilities.md around lines
151 - 165, Remove the instructions directing readers to curl LiteLLM’s upstream
main pricing JSON near the pricing verification guidance. Replace them with
instructions to validate model-prefix pricing against the pinned sdk1
environment and its installed LiteLLM data, keeping the guidance consistent with
production dependencies.

Comment on lines +241 to +251
python .claude/skills/adapter-ops/scripts/check_adapter_updates.py

# Check specific adapter type
python .claude/skills/unstract-adapter-extension/scripts/check_adapter_updates.py --adapter llm
python .claude/skills/unstract-adapter-extension/scripts/check_adapter_updates.py --adapter embedding
python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --adapter llm
python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --adapter embedding

# Check specific provider
python .claude/skills/unstract-adapter-extension/scripts/check_adapter_updates.py --provider openai
python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --provider openai

# Output as JSON
python .claude/skills/unstract-adapter-extension/scripts/check_adapter_updates.py --json
python .claude/skills/adapter-ops/scripts/check_adapter_updates.py --json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make --json output valid JSON.

check_adapter_updates.py prints SDK1 Adapters Path: ... before json.dumps(...), so the documented --json command cannot be parsed by jq or other automation. Send the diagnostic to stderr or suppress it in JSON mode.

🧰 Tools
🪛 SkillSpector (2.3.7)

[warning] 74: [PE2] Sudo/Root Execution: Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Remediation: Avoid sudo/root unless strictly required. Prefer least-privilege patterns. If elevation is needed, document the justification and scope.

(Privilege Escalation (PE2))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/adapter-ops/SKILL.md around lines 241 - 251, Update
check_adapter_updates.py so its --json mode emits only the json.dumps(...)
payload on stdout; route the “SDK1 Adapters Path” diagnostic to stderr or
suppress it when JSON output is enabled, while preserving the diagnostic for
normal output.

`custom_openai/` prefix.

**Common provider names:**
| Display Name | `get_provider()` Value |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required blank line before the table.

The Common provider names label is immediately followed by the table, triggering markdownlint MD058.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 359-359: Tables should be surrounded by blank lines

(MD058, blanks-around-tables)

🪛 SkillSpector (2.3.7)

[warning] 74: [PE2] Sudo/Root Execution: Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Remediation: Avoid sudo/root unless strictly required. Prefer least-privilege patterns. If elevation is needed, document the justification and scope.

(Privilege Escalation (PE2))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/adapter-ops/SKILL.md at line 359, Insert a blank line between
the “Common provider names” label and the table beginning with “Display Name” to
satisfy markdownlint MD058.

Source: Linters/SAST tools

@greptile-apps

greptile-apps Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR updates the adapter-ops skill guidance for adapter cost tracking. The main changes are:

  • Replaces stale skill script paths with the adapter-ops directory.
  • Reframes cost checks around LiteLLM model-prefix resolution.
  • Documents the real LLM and embedding cost lookup paths.
  • Adds guidance for choosing native LiteLLM vs OpenAI-compatible adapter base classes.
  • Updates comments in the adapter initialization scripts for the renamed skill path.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
.claude/skills/adapter-ops/SKILL.md Updates skill commands and replaces stale provider-name billing guidance with model-prefix cost verification.
.claude/skills/adapter-ops/references/adapter_patterns.md Expands adapter authoring guidance around validated model prefixes and base-class selection.
.claude/skills/adapter-ops/references/provider_capabilities.md Updates the provider capability reference to describe cost lookup through prefixed model strings.
.claude/skills/adapter-ops/scripts/init_embedding_adapter.py Updates the repo-root path comment for the renamed skill directory.
.claude/skills/adapter-ops/scripts/init_llm_adapter.py Updates the repo-root path comment for the renamed skill directory.

Reviews (1): Last reviewed commit: "[MISC] Fix adapter-ops skill cost-tracki..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown
Contributor

Unstract test results

Per-group results

Status Group Tier Passed Failed Errors Skipped Duration (s)
e2e-login e2e 2 0 0 0 1.7
e2e-smoke e2e 2 0 0 0 1.1
integration-backend integration 56 0 0 27 48.6
integration-connectors integration 1 0 0 7 8.3
unit-backend unit 118 0 0 0 21.6
unit-connectors unit 63 0 0 0 10.1
unit-core unit 27 0 0 0 1.3
unit-platform-service unit 15 0 0 0 2.7
unit-rig unit 69 0 0 0 3.6
unit-sdk1 unit 381 0 0 0 23.0
unit-workers unit 0 0 0 0 27.8
TOTAL 734 0 0 34 150.0

Critical paths

⚠️ Critical paths not yet covered

  • workflow-create-execute — Create a workflow, configure source+destination, execute, poll, fetch result. (entry: POST /api/v1/workflow/{id}/execute/; declared coverage: e2e-workflow)
  • api-deployment-run — Deploy a workflow as an API, POST a document, receive structured JSON. (entry: POST /deployment/api/{org}/{name}/; declared coverage: e2e-api-deployment)
  • prompt-studio-fetch-response — Prompt Studio: create project, add prompt, run single-pass, get response. (entry: POST /api/v1/prompt-studio/prompt-studio-tool/{id}/fetch_response/; declared coverage: e2e-prompt-studio)
  • pipeline-etl-execute — Run an ETL pipeline from source connector to destination. (entry: POST /api/v1/pipeline/{id}/execute/; declared coverage: no groups declared)
  • usage-token-tracking — Per-execution token usage is recorded and retrievable. (entry: GET /api/v1/usage/get_token_usage/; declared coverage: no groups declared)
  • workflow-execution-fan-out — Multi-file workflow execution fans out to file-processing workers and rejoins. (entry: internal: backend → rabbitmq → workers/file_processing; declared coverage: no groups declared)
  • callback-result-delivery — Async results are posted back via the callback worker. (entry: internal: workers/callback → backend /internal endpoints; declared coverage: no groups declared)
✅ Covered critical paths
  • auth-login — covered by e2e-login
  • adapter-register-llm — covered by integration-backend

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