prompture 1.0.53.dev2__tar.gz → 1.0.54.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prompture-1.0.54.dev1/.claude/skills/update-pricing/SKILL.md +164 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/PKG-INFO +1 -1
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/_version.py +2 -2
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_base.py +2 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_claude_driver.py +31 -6
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_google_driver.py +9 -1
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_grok_driver.py +16 -2
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_groq_driver.py +16 -2
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_moonshot_driver.py +21 -4
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openai_driver.py +23 -8
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/base.py +2 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/claude_driver.py +63 -7
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/google_driver.py +12 -1
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/grok_driver.py +17 -3
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/groq_driver.py +17 -3
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/moonshot_driver.py +45 -3
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openai_driver.py +41 -8
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/cost_mixin.py +34 -6
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/model_rates.py +8 -18
- prompture-1.0.54.dev1/prompture/infra/pricing.py +284 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/anthropic.json +28 -4
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/google.json +42 -7
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/groq.json +44 -8
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/moonshot.json +30 -5
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/openai.json +17 -3
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/xai.json +36 -6
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/settings.py +9 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/tracker.py +32 -2
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/PKG-INFO +1 -1
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/SOURCES.txt +1 -0
- prompture-1.0.53.dev2/.claude/skills/update-pricing/SKILL.md +0 -124
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-driver/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-driver/references/driver-template.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-example/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-field/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-persona/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-test/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/add-tool/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/create-pr/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/run-tests/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.claude/skills/scaffold-extraction/SKILL.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.env.copy +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/FUNDING.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/ISSUE_TEMPLATE/pricing_cost_mismatch.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/ISSUE_TEMPLATE/provider_driver_bug.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/ISSUE_TEMPLATE/schema_extraction_failure.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/scripts/update_docs_version.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/scripts/update_wrapper_version.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/workflows/dev.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/workflows/documentation.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/workflows/pricing-freshness.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/workflows/publish.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.github/workflows/security.yml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/.safety-project.ini +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/AGENTS.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/BREAKING_CHANGES.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/CLAUDE.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/CONTRIBUTING.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/LICENSE +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/MANIFEST.in +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/README.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/ROADMAP.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/SECURITY.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/dev.ps1 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/dev.sh +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/mypy_errors.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/README.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_json/README.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_json/llm_to_json/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_json/pyproject.toml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_json/test.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_toon/README.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_toon/llm_to_toon/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_toon/pyproject.toml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/packages/llm_to_toon/test.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/agent.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/async_agent.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/async_conversation.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/conversation.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/persona.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/simulated_tools.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/skills.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/tools_schema.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/agents/types.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/aio/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/bridges/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/bridges/tukuy_backend.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/cli/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/cli/cli.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/cli/formatters.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/cli/runner.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/cli/server.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/airllm_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_airllm_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_azure_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_cachibot_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_elevenlabs_stt_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_elevenlabs_tts_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_embedding_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_google_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_google_vertexai_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_grok_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_hugging_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_img_gen_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_lmstudio_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_local_http_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_modelscope_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_ollama_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_ollama_embedding_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openai_embedding_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openai_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openai_stt_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openai_tts_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_openrouter_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_stability_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_stt_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_tts_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/async_zai_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/audio_registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/azure_config.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/azure_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/cachibot_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/driver_registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/elevenlabs_stt_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/elevenlabs_tts_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/embedding_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/embedding_registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/google_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/google_vertexai_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/grok_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/hugging_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/img_gen_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/img_gen_registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/lmstudio_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/local_http_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/modelscope_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/ollama_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/ollama_embedding_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openai_embedding_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openai_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openai_stt_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openai_tts_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/openrouter_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/provider_descriptors.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/registry.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/stability_img_gen_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/stt_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/tts_base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/vision_helpers.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/drivers/zai_driver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/exceptions.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/async_core.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/core.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/fields.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/reasoning.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/strategy.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/tools.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/tukuy_bridge.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/extraction/validator.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/async_groups.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/consensus.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/debate.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/groups.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/groups/types.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/budget.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/cache.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/callbacks.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/capabilities.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/discovery.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/ledger.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/logging.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/provider_env.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/azure.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/google_vertexai.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/rates/openrouter.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/session.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/infra/tukuy_backend.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/chunking.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/detect.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/document.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/base.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/csv_parser.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/docx.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/html.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/markdown.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/pdf.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/ingestion/parsers/xlsx.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/integrations/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/integrations/tukuy_bridge.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/media/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/media/audio.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/media/image.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/01-why-prompture.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/02-installation.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/03-quickstart.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/04-toon-input.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/05-test-harness.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/06-stability.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pages/07-migration.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/persistence/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/persistence/history.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/persistence/serialization.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/persistence/store.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pipeline/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pipeline/pipeline.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pipeline/resolver.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/pipeline/routing.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/py.typed +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/generator.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/Dockerfile.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/README.md.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/config.py.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/env.example.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/main.py.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/models.py.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/scaffold/templates/requirements.txt.j2 +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/testing/__init__.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture/testing/driver_contract.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/dependency_links.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/entry_points.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/requires.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture.egg-info/top_level.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/prompture_cost_tracking.md +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/pyproject.toml +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/requirements.txt +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/scripts/check_pricing_freshness.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/setup.cfg +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/specs/basic_extraction.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/specs/schema_validation.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/specs/strategy_comparison.json +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/test.py +0 -0
- {prompture-1.0.53.dev2 → prompture-1.0.54.dev1}/test_version_diagnosis.py +0 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update-pricing
|
|
3
|
+
description: Update LLM model pricing in Prompture. Pricing resolves through a pluggable source registry — by default local KB JSON files (primary, curated) then models.dev (fallback). Use when model prices change, new models launch, models.dev data is stale, or you need to add a custom pricing source.
|
|
4
|
+
metadata:
|
|
5
|
+
author: prompture
|
|
6
|
+
version: "4.0"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Update Model Pricing
|
|
10
|
+
|
|
11
|
+
## How Pricing Resolves
|
|
12
|
+
|
|
13
|
+
Pricing flows through a **pluggable source registry** at `prompture/infra/pricing.py`. `get_model_rates(provider, model_id)` walks the registered sources in priority order (lowest first) and returns the first dict that includes both `input` and `output` per-1M rates.
|
|
14
|
+
|
|
15
|
+
Default registration (controlled by `Settings.pricing_source`):
|
|
16
|
+
|
|
17
|
+
| Setting value | Sources registered (priority order) |
|
|
18
|
+
|---------------|-------------------------------------|
|
|
19
|
+
| `local_first` (default) | `LocalKBPricingSource` (priority 0) → `ModelsDevPricingSource` (priority 100) |
|
|
20
|
+
| `local_only` | `LocalKBPricingSource` only |
|
|
21
|
+
| `models_dev_only` | `ModelsDevPricingSource` only |
|
|
22
|
+
|
|
23
|
+
**Use `local_first` (default)** when you want curated rates with automatic fallback. **`local_only`** for offline/locked-down envs or when you don't trust models.dev. **`models_dev_only`** as a temporary opt-out while local files are unmaintained.
|
|
24
|
+
|
|
25
|
+
`CostMixin._calculate_cost()` consumes the resolved dict — `input`, `output`, optional `cache_read`, `cache_write`, `reasoning` rates per 1M tokens. Cached prompt tokens are billed at `cache_read` (falls back to `input` rate when not published).
|
|
26
|
+
|
|
27
|
+
## Local KB layout (the primary source)
|
|
28
|
+
|
|
29
|
+
Per-provider JSON files in `prompture/infra/rates/` carry both **capabilities** and **pricing** for each model. Adding a `cost` block makes that model use local pricing; omitting it falls through to models.dev.
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"gpt-5.5": {
|
|
34
|
+
"supports_temperature": false,
|
|
35
|
+
"supports_tool_use": true,
|
|
36
|
+
"supports_structured_output": true,
|
|
37
|
+
"supports_vision": true,
|
|
38
|
+
"is_reasoning": true,
|
|
39
|
+
"context_window": 1050000,
|
|
40
|
+
"max_output_tokens": 128000,
|
|
41
|
+
"modalities_input": ["text", "image"],
|
|
42
|
+
"modalities_output": ["text"],
|
|
43
|
+
"api_type": "openai",
|
|
44
|
+
"tokens_param": "max_completion_tokens",
|
|
45
|
+
"cost": {
|
|
46
|
+
"input": 5.00,
|
|
47
|
+
"output": 30.00,
|
|
48
|
+
"cache_read": 0.50
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**`cost` block keys** (USD per 1M tokens):
|
|
55
|
+
- `input` — required for cost to compute
|
|
56
|
+
- `output` — required
|
|
57
|
+
- `cache_read` — optional. Discount rate for cached prompt tokens (Anthropic / OpenAI / Kimi all support this).
|
|
58
|
+
- `cache_write` — optional. Anthropic only — premium rate for tokens written to prompt cache.
|
|
59
|
+
- `reasoning` — optional. For models that bill reasoning tokens separately.
|
|
60
|
+
|
|
61
|
+
## Vendor pricing pages (verify before editing)
|
|
62
|
+
|
|
63
|
+
**Always cross-check rates against the vendor's official page**, not just models.dev — feeds get stale.
|
|
64
|
+
|
|
65
|
+
| Provider | Pricing page |
|
|
66
|
+
|----------|--------------|
|
|
67
|
+
| OpenAI | https://developers.openai.com/api/docs/pricing |
|
|
68
|
+
| Anthropic (Claude) | https://platform.claude.com/docs/en/about-claude/pricing |
|
|
69
|
+
| Moonshot (Kimi) | https://platform.kimi.ai/docs (search "Model Pricing") |
|
|
70
|
+
| xAI (Grok) | https://docs.x.ai/docs/models |
|
|
71
|
+
| Google (Gemini) | https://ai.google.dev/gemini-api/docs/pricing |
|
|
72
|
+
| Groq | https://groq.com/pricing |
|
|
73
|
+
| OpenRouter | https://openrouter.ai/models |
|
|
74
|
+
| Moonshot K2 | https://platform.moonshot.ai/docs/pricing/chat |
|
|
75
|
+
| Z.ai (GLM) | https://docs.z.ai/guides/llm/pricing |
|
|
76
|
+
|
|
77
|
+
When a vendor publishes "cache hit" / "cache read" pricing, encode it as `cache_read`. When they publish a "cache write" / "cache creation" rate (Anthropic only), encode it as `cache_write`.
|
|
78
|
+
|
|
79
|
+
## When to update what
|
|
80
|
+
|
|
81
|
+
| Scenario | Action |
|
|
82
|
+
|----------|--------|
|
|
83
|
+
| Vendor changed prices for a model already in the KB | Update the `cost` block in `rates/{provider}.json` |
|
|
84
|
+
| New model from existing provider | Add entry with capabilities + `cost` block |
|
|
85
|
+
| New provider | Create `rates/{provider}.json`, add to `PROVIDER_MAP` in `model_rates.py`, wire descriptor in `provider_descriptors.py` |
|
|
86
|
+
| Model not in KB but in models.dev | Nothing — fallback already covers it |
|
|
87
|
+
| You want to override what models.dev says | Add the `cost` block to local KB; it wins |
|
|
88
|
+
| You want to lock pricing to local-only | Set `pricing_source=local_only` in env / `.env` |
|
|
89
|
+
|
|
90
|
+
## Adding a custom pricing source
|
|
91
|
+
|
|
92
|
+
For internal price feeds, vendor APIs the registry doesn't cover, or per-environment overrides:
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from prompture.infra.pricing import register_pricing_source
|
|
96
|
+
|
|
97
|
+
class MyInternalFeed:
|
|
98
|
+
name = "internal_feed"
|
|
99
|
+
priority = 50 # between local_kb (0) and models_dev (100)
|
|
100
|
+
|
|
101
|
+
def get_rates(self, provider: str, model_id: str) -> dict[str, float] | None:
|
|
102
|
+
# Return {"input": ..., "output": ..., "cache_read": ...} or None
|
|
103
|
+
return _query_my_internal_service(provider, model_id)
|
|
104
|
+
|
|
105
|
+
register_pricing_source(MyInternalFeed())
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Sources are ordered by `(priority, name)`. The first to return a dict containing both `input` and `output` wins. Exceptions inside `get_rates` are swallowed and the next source is tried.
|
|
109
|
+
|
|
110
|
+
## Refreshing the models.dev cache
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from prompture.infra.model_rates import refresh_rates_cache
|
|
114
|
+
refresh_rates_cache(force=True)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or delete the cache file:
|
|
118
|
+
```bash
|
|
119
|
+
rm ~/.prompture/cache/models_dev.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Provider name → models.dev key mapping
|
|
123
|
+
|
|
124
|
+
Lives in `prompture/infra/model_rates.py` (`PROVIDER_MAP`):
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
{"openai": "openai", "claude": "anthropic", "google": "google", "groq": "groq",
|
|
128
|
+
"grok": "xai", "azure": "azure", "openrouter": "openrouter",
|
|
129
|
+
"moonshot": "moonshotai", "zai": "zai"}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Free / local drivers (always $0)
|
|
133
|
+
|
|
134
|
+
`ollama_driver.py`, `lmstudio_driver.py`, `local_http_driver.py`, `airllm_driver.py`, `hugging_driver.py`.
|
|
135
|
+
|
|
136
|
+
## Steps for adding / updating a model price
|
|
137
|
+
|
|
138
|
+
1. **Open** the vendor's pricing page (table above) and copy the current rates.
|
|
139
|
+
2. **Edit** `prompture/infra/rates/{provider}.json` — find the model entry (or add a new one with capabilities) and add/update the `cost` block.
|
|
140
|
+
3. **Verify** with `python -c "from prompture.infra.model_rates import get_model_rates; print(get_model_rates('openai', 'gpt-5.5'))"`. The dict should include exactly the keys you wrote.
|
|
141
|
+
4. **Run tests**: `pytest tests/test_pricing.py tests/test_cached_tokens.py -q`
|
|
142
|
+
5. **For long-running processes** that imported the LocalKBPricingSource before the edit: the in-memory KB is loaded once at startup. Either restart, or call `LocalKBPricingSource().reload()` on the registered instance.
|
|
143
|
+
|
|
144
|
+
## Verification commands
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Check resolved rates for a model
|
|
148
|
+
python -c "from prompture.infra.model_rates import get_model_rates; print(get_model_rates('openai', 'gpt-5.5'))"
|
|
149
|
+
|
|
150
|
+
# Inspect which sources are registered
|
|
151
|
+
python -c "from prompture.infra.pricing import get_pricing_sources; print([(s.name, s.priority) for s in get_pricing_sources()])"
|
|
152
|
+
|
|
153
|
+
# Check that capabilities still load
|
|
154
|
+
python -c "from prompture.infra.model_rates import get_model_capabilities; print(get_model_capabilities('openai', 'gpt-5.5'))"
|
|
155
|
+
|
|
156
|
+
# Run pricing + cost tests
|
|
157
|
+
pytest tests/test_pricing.py tests/test_cached_tokens.py tests/test_model_rates.py -q
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Side effects of editing the KB
|
|
161
|
+
|
|
162
|
+
- `prompture/infra/discovery.py` reads KB model IDs via `get_kb_models_for_provider()` for static detection (no API key needed).
|
|
163
|
+
- `CostMixin._get_model_config()` still reads `tokens_param` / `supports_temperature` from the KB capabilities.
|
|
164
|
+
- A model with a local `cost` block now appears in cost calculations regardless of models.dev cache state — useful when models.dev lags vendor announcements.
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 0,
|
|
21
|
+
__version__ = version = '1.0.54.dev1'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 54, 'dev1')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -232,6 +232,8 @@ class AsyncDriver(ABC):
|
|
|
232
232
|
prompt_tokens=meta.get("prompt_tokens", 0),
|
|
233
233
|
completion_tokens=meta.get("completion_tokens", 0),
|
|
234
234
|
total_tokens=meta.get("total_tokens", 0),
|
|
235
|
+
cached_prompt_tokens=meta.get("cached_prompt_tokens", 0),
|
|
236
|
+
cache_creation_tokens=meta.get("cache_creation_tokens", 0),
|
|
235
237
|
cost=meta.get("cost", 0.0),
|
|
236
238
|
elapsed_ms=elapsed_ms,
|
|
237
239
|
status=status,
|
|
@@ -20,6 +20,7 @@ from .claude_driver import (
|
|
|
20
20
|
_build_anthropic_meta,
|
|
21
21
|
_build_anthropic_stream_done,
|
|
22
22
|
_convert_tools_to_anthropic,
|
|
23
|
+
_extract_anthropic_cache_tokens,
|
|
23
24
|
_extract_anthropic_system_and_messages,
|
|
24
25
|
_extract_anthropic_text_and_tool_calls,
|
|
25
26
|
)
|
|
@@ -104,8 +105,14 @@ class AsyncClaudeDriver(CostMixin, AsyncDriver):
|
|
|
104
105
|
if not text and reasoning_content:
|
|
105
106
|
text = reasoning_content
|
|
106
107
|
|
|
108
|
+
cache_read, cache_create = _extract_anthropic_cache_tokens(resp.usage)
|
|
107
109
|
total_cost = self._calculate_cost(
|
|
108
|
-
"claude",
|
|
110
|
+
"claude",
|
|
111
|
+
model,
|
|
112
|
+
resp.usage.input_tokens + cache_read + cache_create,
|
|
113
|
+
resp.usage.output_tokens,
|
|
114
|
+
cached_tokens=cache_read,
|
|
115
|
+
cache_creation_tokens=cache_create,
|
|
109
116
|
)
|
|
110
117
|
meta = _build_anthropic_meta(resp, model, total_cost)
|
|
111
118
|
|
|
@@ -157,8 +164,14 @@ class AsyncClaudeDriver(CostMixin, AsyncDriver):
|
|
|
157
164
|
|
|
158
165
|
resp = await client.messages.create(**kwargs)
|
|
159
166
|
|
|
167
|
+
cache_read, cache_create = _extract_anthropic_cache_tokens(resp.usage)
|
|
160
168
|
total_cost = self._calculate_cost(
|
|
161
|
-
"claude",
|
|
169
|
+
"claude",
|
|
170
|
+
model,
|
|
171
|
+
resp.usage.input_tokens + cache_read + cache_create,
|
|
172
|
+
resp.usage.output_tokens,
|
|
173
|
+
cached_tokens=cache_read,
|
|
174
|
+
cache_creation_tokens=cache_create,
|
|
162
175
|
)
|
|
163
176
|
meta = _build_anthropic_meta(resp, model, total_cost)
|
|
164
177
|
|
|
@@ -205,8 +218,10 @@ class AsyncClaudeDriver(CostMixin, AsyncDriver):
|
|
|
205
218
|
|
|
206
219
|
full_text = ""
|
|
207
220
|
full_reasoning = ""
|
|
208
|
-
|
|
221
|
+
base_input = 0
|
|
209
222
|
completion_tokens = 0
|
|
223
|
+
cache_read = 0
|
|
224
|
+
cache_create = 0
|
|
210
225
|
|
|
211
226
|
async with client.messages.stream(**kwargs) as stream:
|
|
212
227
|
async for event in stream:
|
|
@@ -228,9 +243,19 @@ class AsyncClaudeDriver(CostMixin, AsyncDriver):
|
|
|
228
243
|
elif event.type == "message_start" and hasattr(event, "message"):
|
|
229
244
|
usage = getattr(event.message, "usage", None)
|
|
230
245
|
if usage:
|
|
231
|
-
|
|
246
|
+
base_input = getattr(usage, "input_tokens", 0)
|
|
247
|
+
cache_read, cache_create = _extract_anthropic_cache_tokens(usage)
|
|
232
248
|
|
|
233
|
-
|
|
249
|
+
prompt_tokens = base_input + cache_read + cache_create
|
|
250
|
+
total_cost = self._calculate_cost(
|
|
251
|
+
"claude",
|
|
252
|
+
model,
|
|
253
|
+
prompt_tokens,
|
|
254
|
+
completion_tokens,
|
|
255
|
+
cached_tokens=cache_read,
|
|
256
|
+
cache_creation_tokens=cache_create,
|
|
257
|
+
)
|
|
234
258
|
yield _build_anthropic_stream_done(
|
|
235
|
-
model, full_text, full_reasoning, prompt_tokens, completion_tokens, total_cost
|
|
259
|
+
model, full_text, full_reasoning, prompt_tokens, completion_tokens, total_cost,
|
|
260
|
+
cached_prompt_tokens=cache_read, cache_creation_tokens=cache_create,
|
|
236
261
|
)
|
|
@@ -64,11 +64,18 @@ class AsyncGoogleDriver(CostMixin, AsyncDriver):
|
|
|
64
64
|
def _extract_usage_metadata(self, response: Any, messages: list[dict[str, Any]]) -> dict[str, Any]:
|
|
65
65
|
"""Extract token counts from response, falling back to character estimation."""
|
|
66
66
|
usage = getattr(response, "usage_metadata", None)
|
|
67
|
+
cached_prompt_tokens = 0
|
|
67
68
|
if usage:
|
|
68
69
|
prompt_tokens = getattr(usage, "prompt_token_count", 0) or 0
|
|
69
70
|
completion_tokens = getattr(usage, "candidates_token_count", 0) or 0
|
|
70
71
|
total_tokens = getattr(usage, "total_token_count", 0) or (prompt_tokens + completion_tokens)
|
|
71
|
-
|
|
72
|
+
raw_cached = getattr(usage, "cached_content_token_count", 0) or 0
|
|
73
|
+
if isinstance(raw_cached, (int, float)):
|
|
74
|
+
cached_prompt_tokens = int(raw_cached)
|
|
75
|
+
cost = self._calculate_cost(
|
|
76
|
+
"google", self.model, prompt_tokens, completion_tokens,
|
|
77
|
+
cached_tokens=cached_prompt_tokens,
|
|
78
|
+
)
|
|
72
79
|
else:
|
|
73
80
|
# Fallback: estimate from character counts
|
|
74
81
|
total_prompt_chars = 0
|
|
@@ -92,6 +99,7 @@ class AsyncGoogleDriver(CostMixin, AsyncDriver):
|
|
|
92
99
|
"prompt_tokens": prompt_tokens,
|
|
93
100
|
"completion_tokens": completion_tokens,
|
|
94
101
|
"total_tokens": total_tokens,
|
|
102
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
95
103
|
"cost": round(cost, 6),
|
|
96
104
|
}
|
|
97
105
|
|
|
@@ -81,17 +81,24 @@ class AsyncGrokDriver(CostMixin, AsyncDriver):
|
|
|
81
81
|
except Exception as e:
|
|
82
82
|
raise RuntimeError(f"Grok API request failed: {e!s}") from e
|
|
83
83
|
|
|
84
|
+
from .moonshot_driver import _extract_moonshot_cached_tokens
|
|
85
|
+
|
|
84
86
|
usage = resp.get("usage", {})
|
|
85
87
|
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
86
88
|
completion_tokens = usage.get("completion_tokens", 0)
|
|
87
89
|
total_tokens = usage.get("total_tokens", 0)
|
|
90
|
+
cached_prompt_tokens = _extract_moonshot_cached_tokens(usage)
|
|
88
91
|
|
|
89
|
-
total_cost = self._calculate_cost(
|
|
92
|
+
total_cost = self._calculate_cost(
|
|
93
|
+
"grok", model, prompt_tokens, completion_tokens,
|
|
94
|
+
cached_tokens=cached_prompt_tokens,
|
|
95
|
+
)
|
|
90
96
|
|
|
91
97
|
meta = {
|
|
92
98
|
"prompt_tokens": prompt_tokens,
|
|
93
99
|
"completion_tokens": completion_tokens,
|
|
94
100
|
"total_tokens": total_tokens,
|
|
101
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
95
102
|
"cost": round(total_cost, 6),
|
|
96
103
|
"raw_response": resp,
|
|
97
104
|
"model_name": model,
|
|
@@ -159,16 +166,23 @@ class AsyncGrokDriver(CostMixin, AsyncDriver):
|
|
|
159
166
|
except Exception as e:
|
|
160
167
|
raise RuntimeError(f"Grok API request failed: {e!s}") from e
|
|
161
168
|
|
|
169
|
+
from .moonshot_driver import _extract_moonshot_cached_tokens
|
|
170
|
+
|
|
162
171
|
usage = resp.get("usage", {})
|
|
163
172
|
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
164
173
|
completion_tokens = usage.get("completion_tokens", 0)
|
|
165
174
|
total_tokens = usage.get("total_tokens", 0)
|
|
166
|
-
|
|
175
|
+
cached_prompt_tokens = _extract_moonshot_cached_tokens(usage)
|
|
176
|
+
total_cost = self._calculate_cost(
|
|
177
|
+
"grok", model, prompt_tokens, completion_tokens,
|
|
178
|
+
cached_tokens=cached_prompt_tokens,
|
|
179
|
+
)
|
|
167
180
|
|
|
168
181
|
meta = {
|
|
169
182
|
"prompt_tokens": prompt_tokens,
|
|
170
183
|
"completion_tokens": completion_tokens,
|
|
171
184
|
"total_tokens": total_tokens,
|
|
185
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
172
186
|
"cost": round(total_cost, 6),
|
|
173
187
|
"raw_response": resp,
|
|
174
188
|
"model_name": model,
|
|
@@ -75,17 +75,24 @@ class AsyncGroqDriver(CostMixin, AsyncDriver):
|
|
|
75
75
|
|
|
76
76
|
resp = await self.client.chat.completions.create(**kwargs)
|
|
77
77
|
|
|
78
|
+
from .openai_driver import _extract_openai_cached_tokens
|
|
79
|
+
|
|
78
80
|
usage = getattr(resp, "usage", None)
|
|
79
81
|
prompt_tokens = getattr(usage, "prompt_tokens", 0)
|
|
80
82
|
completion_tokens = getattr(usage, "completion_tokens", 0)
|
|
81
83
|
total_tokens = getattr(usage, "total_tokens", 0)
|
|
84
|
+
cached_prompt_tokens = _extract_openai_cached_tokens(usage)
|
|
82
85
|
|
|
83
|
-
total_cost = self._calculate_cost(
|
|
86
|
+
total_cost = self._calculate_cost(
|
|
87
|
+
"groq", model, prompt_tokens, completion_tokens,
|
|
88
|
+
cached_tokens=cached_prompt_tokens,
|
|
89
|
+
)
|
|
84
90
|
|
|
85
91
|
meta = {
|
|
86
92
|
"prompt_tokens": prompt_tokens,
|
|
87
93
|
"completion_tokens": completion_tokens,
|
|
88
94
|
"total_tokens": total_tokens,
|
|
95
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
89
96
|
"cost": round(total_cost, 6),
|
|
90
97
|
"raw_response": resp.model_dump(),
|
|
91
98
|
"model_name": model,
|
|
@@ -137,16 +144,23 @@ class AsyncGroqDriver(CostMixin, AsyncDriver):
|
|
|
137
144
|
|
|
138
145
|
resp = await self.client.chat.completions.create(**kwargs)
|
|
139
146
|
|
|
147
|
+
from .openai_driver import _extract_openai_cached_tokens
|
|
148
|
+
|
|
140
149
|
usage = getattr(resp, "usage", None)
|
|
141
150
|
prompt_tokens = getattr(usage, "prompt_tokens", 0)
|
|
142
151
|
completion_tokens = getattr(usage, "completion_tokens", 0)
|
|
143
152
|
total_tokens = getattr(usage, "total_tokens", 0)
|
|
144
|
-
|
|
153
|
+
cached_prompt_tokens = _extract_openai_cached_tokens(usage)
|
|
154
|
+
total_cost = self._calculate_cost(
|
|
155
|
+
"groq", model, prompt_tokens, completion_tokens,
|
|
156
|
+
cached_tokens=cached_prompt_tokens,
|
|
157
|
+
)
|
|
145
158
|
|
|
146
159
|
meta = {
|
|
147
160
|
"prompt_tokens": prompt_tokens,
|
|
148
161
|
"completion_tokens": completion_tokens,
|
|
149
162
|
"total_tokens": total_tokens,
|
|
163
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
150
164
|
"cost": round(total_cost, 6),
|
|
151
165
|
"raw_response": resp.model_dump(),
|
|
152
166
|
"model_name": model,
|
|
@@ -20,7 +20,7 @@ import httpx
|
|
|
20
20
|
from ..infra.cost_mixin import CostMixin, prepare_strict_schema
|
|
21
21
|
from .async_base import AsyncDriver
|
|
22
22
|
from .base import _parse_tool_arguments
|
|
23
|
-
from .moonshot_driver import MoonshotDriver
|
|
23
|
+
from .moonshot_driver import MoonshotDriver, _extract_moonshot_cached_tokens
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger("prompture.drivers.moonshot")
|
|
26
26
|
|
|
@@ -142,6 +142,7 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
142
142
|
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
143
143
|
completion_tokens = usage.get("completion_tokens", 0)
|
|
144
144
|
total_tokens = usage.get("total_tokens", 0)
|
|
145
|
+
cached_prompt_tokens = _extract_moonshot_cached_tokens(usage)
|
|
145
146
|
|
|
146
147
|
message = resp["choices"][0]["message"]
|
|
147
148
|
text = message.get("content") or ""
|
|
@@ -189,6 +190,7 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
189
190
|
prompt_tokens += fb_usage.get("prompt_tokens", 0)
|
|
190
191
|
completion_tokens = fb_usage.get("completion_tokens", 0)
|
|
191
192
|
total_tokens = prompt_tokens + completion_tokens
|
|
193
|
+
cached_prompt_tokens += _extract_moonshot_cached_tokens(fb_usage)
|
|
192
194
|
resp = fb_resp
|
|
193
195
|
fb_message = fb_resp["choices"][0]["message"]
|
|
194
196
|
text = fb_message.get("content") or ""
|
|
@@ -196,12 +198,16 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
196
198
|
if not text and reasoning_content:
|
|
197
199
|
text = reasoning_content
|
|
198
200
|
|
|
199
|
-
total_cost = self._calculate_cost(
|
|
201
|
+
total_cost = self._calculate_cost(
|
|
202
|
+
"moonshot", model, prompt_tokens, completion_tokens,
|
|
203
|
+
cached_tokens=cached_prompt_tokens,
|
|
204
|
+
)
|
|
200
205
|
|
|
201
206
|
meta = {
|
|
202
207
|
"prompt_tokens": prompt_tokens,
|
|
203
208
|
"completion_tokens": completion_tokens,
|
|
204
209
|
"total_tokens": total_tokens,
|
|
210
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
205
211
|
"cost": round(total_cost, 6),
|
|
206
212
|
"raw_response": resp,
|
|
207
213
|
"model_name": model,
|
|
@@ -280,12 +286,17 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
280
286
|
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
281
287
|
completion_tokens = usage.get("completion_tokens", 0)
|
|
282
288
|
total_tokens = usage.get("total_tokens", 0)
|
|
283
|
-
|
|
289
|
+
cached_prompt_tokens = _extract_moonshot_cached_tokens(usage)
|
|
290
|
+
total_cost = self._calculate_cost(
|
|
291
|
+
"moonshot", model, prompt_tokens, completion_tokens,
|
|
292
|
+
cached_tokens=cached_prompt_tokens,
|
|
293
|
+
)
|
|
284
294
|
|
|
285
295
|
meta = {
|
|
286
296
|
"prompt_tokens": prompt_tokens,
|
|
287
297
|
"completion_tokens": completion_tokens,
|
|
288
298
|
"total_tokens": total_tokens,
|
|
299
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
289
300
|
"cost": round(total_cost, 6),
|
|
290
301
|
"raw_response": resp,
|
|
291
302
|
"model_name": model,
|
|
@@ -365,6 +376,7 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
365
376
|
full_reasoning = ""
|
|
366
377
|
prompt_tokens = 0
|
|
367
378
|
completion_tokens = 0
|
|
379
|
+
cached_prompt_tokens = 0
|
|
368
380
|
|
|
369
381
|
async with (
|
|
370
382
|
httpx.AsyncClient() as client,
|
|
@@ -392,6 +404,7 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
392
404
|
if usage:
|
|
393
405
|
prompt_tokens = usage.get("prompt_tokens", 0)
|
|
394
406
|
completion_tokens = usage.get("completion_tokens", 0)
|
|
407
|
+
cached_prompt_tokens = _extract_moonshot_cached_tokens(usage)
|
|
395
408
|
|
|
396
409
|
choices = chunk.get("choices", [])
|
|
397
410
|
if choices:
|
|
@@ -407,7 +420,10 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
407
420
|
yield {"type": "delta", "text": content}
|
|
408
421
|
|
|
409
422
|
total_tokens = prompt_tokens + completion_tokens
|
|
410
|
-
total_cost = self._calculate_cost(
|
|
423
|
+
total_cost = self._calculate_cost(
|
|
424
|
+
"moonshot", model, prompt_tokens, completion_tokens,
|
|
425
|
+
cached_tokens=cached_prompt_tokens,
|
|
426
|
+
)
|
|
411
427
|
|
|
412
428
|
done_chunk: dict[str, Any] = {
|
|
413
429
|
"type": "done",
|
|
@@ -416,6 +432,7 @@ class AsyncMoonshotDriver(CostMixin, AsyncDriver):
|
|
|
416
432
|
"prompt_tokens": prompt_tokens,
|
|
417
433
|
"completion_tokens": completion_tokens,
|
|
418
434
|
"total_tokens": total_tokens,
|
|
435
|
+
"cached_prompt_tokens": cached_prompt_tokens,
|
|
419
436
|
"cost": round(total_cost, 6),
|
|
420
437
|
"raw_response": {},
|
|
421
438
|
"model_name": model,
|
|
@@ -19,6 +19,7 @@ from .openai_driver import (
|
|
|
19
19
|
_build_openai_base_kwargs,
|
|
20
20
|
_build_openai_json_mode_response_format,
|
|
21
21
|
_build_openai_stream_done,
|
|
22
|
+
_extract_openai_cached_tokens,
|
|
22
23
|
_extract_openai_meta,
|
|
23
24
|
_extract_openai_tool_calls,
|
|
24
25
|
)
|
|
@@ -90,9 +91,13 @@ class AsyncOpenAIDriver(CostMixin, AsyncDriver):
|
|
|
90
91
|
|
|
91
92
|
resp = await self.client.chat.completions.create(**kwargs)
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
usage = getattr(resp, "usage", None)
|
|
95
|
+
prompt_tokens = getattr(usage, "prompt_tokens", 0)
|
|
96
|
+
completion_tokens = getattr(usage, "completion_tokens", 0)
|
|
97
|
+
cached_prompt_tokens = _extract_openai_cached_tokens(usage)
|
|
98
|
+
total_cost = self._calculate_cost(
|
|
99
|
+
"openai", model, prompt_tokens, completion_tokens, cached_tokens=cached_prompt_tokens
|
|
100
|
+
)
|
|
96
101
|
meta = _extract_openai_meta(resp, model, total_cost)
|
|
97
102
|
|
|
98
103
|
text = resp.choices[0].message.content
|
|
@@ -126,9 +131,13 @@ class AsyncOpenAIDriver(CostMixin, AsyncDriver):
|
|
|
126
131
|
|
|
127
132
|
resp = await self.client.chat.completions.create(**kwargs)
|
|
128
133
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
134
|
+
usage = getattr(resp, "usage", None)
|
|
135
|
+
prompt_tokens = getattr(usage, "prompt_tokens", 0)
|
|
136
|
+
completion_tokens = getattr(usage, "completion_tokens", 0)
|
|
137
|
+
cached_prompt_tokens = _extract_openai_cached_tokens(usage)
|
|
138
|
+
total_cost = self._calculate_cost(
|
|
139
|
+
"openai", model, prompt_tokens, completion_tokens, cached_tokens=cached_prompt_tokens
|
|
140
|
+
)
|
|
132
141
|
meta = _extract_openai_meta(resp, model, total_cost)
|
|
133
142
|
|
|
134
143
|
choice = resp.choices[0]
|
|
@@ -177,12 +186,14 @@ class AsyncOpenAIDriver(CostMixin, AsyncDriver):
|
|
|
177
186
|
full_text = ""
|
|
178
187
|
prompt_tokens = 0
|
|
179
188
|
completion_tokens = 0
|
|
189
|
+
cached_prompt_tokens = 0
|
|
180
190
|
|
|
181
191
|
async for chunk in stream:
|
|
182
192
|
# Usage comes in the final chunk
|
|
183
193
|
if getattr(chunk, "usage", None):
|
|
184
194
|
prompt_tokens = chunk.usage.prompt_tokens or 0
|
|
185
195
|
completion_tokens = chunk.usage.completion_tokens or 0
|
|
196
|
+
cached_prompt_tokens = _extract_openai_cached_tokens(chunk.usage)
|
|
186
197
|
|
|
187
198
|
if chunk.choices:
|
|
188
199
|
delta = chunk.choices[0].delta
|
|
@@ -191,5 +202,9 @@ class AsyncOpenAIDriver(CostMixin, AsyncDriver):
|
|
|
191
202
|
full_text += content
|
|
192
203
|
yield {"type": "delta", "text": content}
|
|
193
204
|
|
|
194
|
-
total_cost = self._calculate_cost(
|
|
195
|
-
|
|
205
|
+
total_cost = self._calculate_cost(
|
|
206
|
+
"openai", model, prompt_tokens, completion_tokens, cached_tokens=cached_prompt_tokens
|
|
207
|
+
)
|
|
208
|
+
yield _build_openai_stream_done(
|
|
209
|
+
model, full_text, prompt_tokens, completion_tokens, total_cost, cached_prompt_tokens
|
|
210
|
+
)
|
|
@@ -402,6 +402,8 @@ class Driver(ABC):
|
|
|
402
402
|
prompt_tokens=meta.get("prompt_tokens", 0),
|
|
403
403
|
completion_tokens=meta.get("completion_tokens", 0),
|
|
404
404
|
total_tokens=meta.get("total_tokens", 0),
|
|
405
|
+
cached_prompt_tokens=meta.get("cached_prompt_tokens", 0),
|
|
406
|
+
cache_creation_tokens=meta.get("cache_creation_tokens", 0),
|
|
405
407
|
cost=meta.get("cost", 0.0),
|
|
406
408
|
elapsed_ms=elapsed_ms,
|
|
407
409
|
status=status,
|