chatlas 0.9.1__tar.gz → 0.10.0__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.
Potentially problematic release.
This version of chatlas might be problematic. Click here for more details.
- {chatlas-0.9.1 → chatlas-0.10.0}/CHANGELOG.md +28 -0
- chatlas-0.10.0/CLAUDE.md +167 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/PKG-INFO +4 -4
- {chatlas-0.9.1 → chatlas-0.10.0}/README.md +2 -2
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/__init__.py +21 -9
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_auto.py +9 -9
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_chat.py +38 -9
- chatlas-0.9.1/chatlas/_anthropic.py → chatlas-0.10.0/chatlas/_provider_anthropic.py +13 -5
- chatlas-0.10.0/chatlas/_provider_cloudflare.py +165 -0
- chatlas-0.9.1/chatlas/_databricks.py → chatlas-0.10.0/chatlas/_provider_databricks.py +13 -2
- chatlas-0.10.0/chatlas/_provider_deepseek.py +171 -0
- chatlas-0.9.1/chatlas/_github.py → chatlas-0.10.0/chatlas/_provider_github.py +2 -2
- chatlas-0.9.1/chatlas/_google.py → chatlas-0.10.0/chatlas/_provider_google.py +5 -5
- chatlas-0.9.1/chatlas/_groq.py → chatlas-0.10.0/chatlas/_provider_groq.py +2 -2
- chatlas-0.10.0/chatlas/_provider_huggingface.py +155 -0
- chatlas-0.10.0/chatlas/_provider_mistral.py +181 -0
- chatlas-0.9.1/chatlas/_ollama.py → chatlas-0.10.0/chatlas/_provider_ollama.py +2 -2
- chatlas-0.9.1/chatlas/_openai.py → chatlas-0.10.0/chatlas/_provider_openai.py +28 -9
- chatlas-0.10.0/chatlas/_provider_openrouter.py +149 -0
- chatlas-0.9.1/chatlas/_perplexity.py → chatlas-0.10.0/chatlas/_provider_perplexity.py +2 -2
- chatlas-0.10.0/chatlas/_provider_portkey.py +123 -0
- chatlas-0.9.1/chatlas/_snowflake.py → chatlas-0.10.0/chatlas/_provider_snowflake.py +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_tokens.py +27 -12
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_turn.py +3 -4
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_typing_extensions.py +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_version.py +16 -3
- chatlas-0.10.0/chatlas/data/prices.json +2870 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/__init__.py +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/anthropic/_client.py +1 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/anthropic/_client_bedrock.py +1 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/anthropic/_submit.py +5 -5
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/google/_submit.py +23 -29
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/openai/_client.py +1 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/openai/_client_azure.py +1 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/openai/_submit.py +28 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/_quarto.yml +8 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/debug.qmd +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/models.qmd +22 -12
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/monitor.qmd +3 -5
- {chatlas-0.9.1 → chatlas-0.10.0}/pyproject.toml +3 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/scripts/_generate_google_types.py +2 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/scripts/_generate_openai_types.py +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/scripts/_utils.py +19 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/__snapshots__/test_chat.ambr +2 -2
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_auto.py +3 -3
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_chat.py +15 -10
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_anthropic.py +1 -1
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_azure.py +3 -3
- chatlas-0.10.0/tests/test_provider_bedrock.py +73 -0
- chatlas-0.10.0/tests/test_provider_cloudflare.py +79 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_databricks.py +1 -1
- chatlas-0.10.0/tests/test_provider_deepseek.py +62 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_google.py +4 -3
- chatlas-0.10.0/tests/test_provider_huggingface.py +103 -0
- chatlas-0.10.0/tests/test_provider_mistral.py +73 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_openai.py +1 -1
- chatlas-0.10.0/tests/test_provider_openrouter.py +68 -0
- chatlas-0.10.0/tests/test_provider_portkey.py +91 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_tokens.py +62 -17
- chatlas-0.9.1/chatlas/data/prices.json +0 -264
- chatlas-0.9.1/tests/test_provider_bedrock.py +0 -72
- {chatlas-0.9.1 → chatlas-0.10.0}/.github/workflows/check-update-types.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.github/workflows/docs-publish.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.github/workflows/release.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.github/workflows/test.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.github/workflows/update-pricing.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.gitignore +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.vscode/extensions.json +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/.vscode/settings.json +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/LICENSE +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/Makefile +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_callbacks.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_content.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_content_image.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_content_pdf.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_display.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_interpolate.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_live_render.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_logging.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_mcp_manager.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_merge.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_provider.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_tokens_old.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_tools.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/_utils.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/py.typed +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/anthropic/__init__.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/google/__init__.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/google/_client.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/chatlas/types/openai/__init__.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/.gitignore +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/_extensions/machow/interlinks/.gitignore +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/_sidebar.yml +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/congressional-assets.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/async.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/chat.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/chatbots.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/parameters.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/stream.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/structured-data.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/system-prompt.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/get-started/tools.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chat-app.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chat-console.mp4 +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chat-console.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chat-notebook.mp4 +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chat-parameters.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chatbot-gradio.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chatbot-shiny.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chatbot-streamlit.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chatbot-textual.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/chatlas-hello.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/client-parameters.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/congressional-assets.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/hello-chat-console.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/model-parameters.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/model-type-hints.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/posit-logo.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/shiny-mcp-run-python.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/shiny-tool-call-display.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/shiny-tool-call-map.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/tool-calling-right.svg +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/images/tool-calling-wrong.svg +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/index.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/logos/hero/hero-old.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/logos/hero/hero.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/logos/hex/logo.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/logos/small/logo.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/misc/RAG.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/misc/examples.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/misc/mcp-tools.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/misc/vocabulary.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/Chat.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatAnthropic.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatAuto.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatAzureOpenAI.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatBedrockAnthropic.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatDatabricks.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatGithub.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatGoogle.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatGroq.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatOllama.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatOpenAI.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatPerplexity.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatSnowflake.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ChatVertex.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/Provider.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/Tool.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/ToolRejectError.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/Turn.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/content_image_file.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/content_image_plot.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/content_image_url.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/content_pdf_file.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/content_pdf_url.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/image_file.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/image_plot.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/image_url.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/index.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/interpolate.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/interpolate_file.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/token_usage.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ChatResponse.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ChatResponseAsync.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.Content.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentImage.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentImageInline.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentImageRemote.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentJson.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentText.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentToolRequest.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ContentToolResult.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.ImageContentTypes.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.MISSING.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.MISSING_TYPE.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.SubmitInputArgsT.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/reference/types.TokenUsage.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/structured-data/article-summary.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/structured-data/classification.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/structured-data/entity-recognition.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/structured-data/multi-modal.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/structured-data/sentiment-analysis.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/styles.scss +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/tool-calling/approval.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/tool-calling/displays.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/tool-calling/how-it-works.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/docs/why-chatlas.qmd +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/pytest.ini +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/scripts/_generate_anthropic_types.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/scripts/main.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/__init__.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/apples.pdf +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/conftest.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/images/dice.png +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/mcp_servers/http_add.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/mcp_servers/http_current_date.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/mcp_servers/stdio_current_date.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/mcp_servers/stdio_subtract_multiply.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_callbacks.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_content.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_content_html.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_content_image.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_content_pdf.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_content_tools.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_interpolate.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_mcp_client.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_provider_snowflake.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_set_model_params.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_tool_from_mcp.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_tools_enhanced.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_turns.py +0 -0
- {chatlas-0.9.1 → chatlas-0.10.0}/tests/test_utils_merge.py +0 -0
|
@@ -7,6 +7,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
-->
|
|
9
9
|
|
|
10
|
+
## [0.10.0] - 2025-08-19
|
|
11
|
+
|
|
12
|
+
### New features
|
|
13
|
+
|
|
14
|
+
* Added `ChatCloudflare()` for chatting via [Cloudflare AI](https://developers.cloudflare.com/workers-ai/get-started/rest-api/). (#150)
|
|
15
|
+
* Added `ChatDeepSeek()` for chatting via [DeepSeek](https://www.deepseek.com/). (#147)
|
|
16
|
+
* Added `ChatOpenRouter()` for chatting via [Open Router](https://openrouter.ai/). (#148)
|
|
17
|
+
* Added `ChatHuggingFace()` for chatting via [Hugging Face](https://huggingface.co/). (#144)
|
|
18
|
+
* Added `ChatMistral()` for chatting via [Mistral AI](https://mistral.ai/). (#145)
|
|
19
|
+
* Added `ChatPortkey()` for chatting via [Portkey AI](https://portkey.ai/). (#143)
|
|
20
|
+
|
|
21
|
+
### Changes
|
|
22
|
+
|
|
23
|
+
* `ChatAnthropic()` and `ChatBedrockAnthropic()` now default to Claude Sonnet 4.0.
|
|
24
|
+
|
|
25
|
+
### Bug fixes
|
|
26
|
+
|
|
27
|
+
* Fixed an issue where chatting with some models was leading to `KeyError: 'cached_input'`. (#149)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [0.9.2] - 2025-08-08
|
|
31
|
+
|
|
32
|
+
### Improvements
|
|
33
|
+
|
|
34
|
+
* `Chat.get_cost()` now covers many more models and also takes cached tokens into account. (#133)
|
|
35
|
+
* Avoid erroring when tool calls occur with recent versions of `openai` (> v1.99.5). (#141)
|
|
36
|
+
|
|
37
|
+
|
|
10
38
|
## [0.9.1] - 2025-07-09
|
|
11
39
|
|
|
12
40
|
### Bug fixes
|
chatlas-0.10.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Development Commands
|
|
6
|
+
|
|
7
|
+
The project uses `uv` for package management and Make for common tasks:
|
|
8
|
+
|
|
9
|
+
- **Setup environment**: `make setup` (installs all dependencies with `uv sync --all-extras`)
|
|
10
|
+
- **Run tests**: `make check-tests` or `uv run pytest`
|
|
11
|
+
- **Type checking**: `make check-types` or `uv run pyright`
|
|
12
|
+
- **Linting/formatting**: `make check-format` (check) or `make format` (fix)
|
|
13
|
+
- **Full checks**: `make check` (runs format, type, and test checks)
|
|
14
|
+
- **Build package**: `make build` (creates dist/ with built package)
|
|
15
|
+
- **Run single test**: `uv run pytest tests/test_specific_file.py::TestClass::test_method -v`
|
|
16
|
+
- **Update snapshots**: `make update-snaps` (for syrupy snapshot tests)
|
|
17
|
+
- **Documentation**: `make docs` (build) or `make docs-preview` (serve locally)
|
|
18
|
+
|
|
19
|
+
## Project Architecture
|
|
20
|
+
|
|
21
|
+
### Core Components
|
|
22
|
+
|
|
23
|
+
**Chat System**: The main `Chat` class in `_chat.py` manages conversation state and provider interactions. It's a generic class that works with different providers through the `Provider` abstract base class.
|
|
24
|
+
|
|
25
|
+
**Provider Pattern**: All LLM providers (OpenAI, Anthropic, Google, etc.) inherit from `Provider` in `_provider.py`. Each provider (e.g., `_provider_openai.py`) implements:
|
|
26
|
+
- Model-specific parameter handling
|
|
27
|
+
- API client configuration
|
|
28
|
+
- Request/response transformation
|
|
29
|
+
- Tool calling integration
|
|
30
|
+
|
|
31
|
+
**Content System**: The `_content.py` module defines structured content types:
|
|
32
|
+
- `ContentText`: Plain text messages
|
|
33
|
+
- `ContentImage`: Image content (inline, remote, or file-based)
|
|
34
|
+
- `ContentToolRequest`/`ContentToolResult`: Tool interaction messages
|
|
35
|
+
- `ContentJson`: Structured data responses
|
|
36
|
+
|
|
37
|
+
**Tool System**: Tools are defined in `_tools.py` and allow LLMs to call Python functions. The system supports:
|
|
38
|
+
- Function registration with automatic schema generation
|
|
39
|
+
- Tool approval workflows
|
|
40
|
+
- MCP (Model Context Protocol) server integration via `_mcp_manager.py`
|
|
41
|
+
|
|
42
|
+
**Turn Management**: `Turn` objects in `_turn.py` represent individual conversation exchanges, containing sequences of `Content` objects.
|
|
43
|
+
|
|
44
|
+
### Key Patterns
|
|
45
|
+
|
|
46
|
+
1. **Provider Abstraction**: All providers implement the same interface but handle model-specific details internally
|
|
47
|
+
2. **Generic Typing**: Heavy use of TypeVars and generics for type safety across providers
|
|
48
|
+
3. **Streaming Support**: Both sync and async streaming responses via `ChatResponse`/`ChatResponseAsync`
|
|
49
|
+
4. **Content-Based Messaging**: All communication uses structured `Content` objects rather than raw strings
|
|
50
|
+
5. **Tool Integration**: Seamless function calling with automatic JSON schema generation from Python type hints
|
|
51
|
+
|
|
52
|
+
### Testing Structure
|
|
53
|
+
|
|
54
|
+
- Tests are organized by component (e.g., `test_provider_openai.py`, `test_tools.py`)
|
|
55
|
+
- Snapshot testing with `syrupy` for response validation
|
|
56
|
+
- MCP server tests use local test servers in `tests/mcp_servers/`
|
|
57
|
+
- Async tests configured via `pytest.ini` with `asyncio_mode=strict`
|
|
58
|
+
|
|
59
|
+
### Documentation
|
|
60
|
+
|
|
61
|
+
Documentation is built with Quarto and quartodoc:
|
|
62
|
+
- API reference generated from docstrings in `chatlas/` modules
|
|
63
|
+
- Guides and examples in `docs/` as `.qmd` files
|
|
64
|
+
- Type definitions in `chatlas/types/` provide provider-specific parameter types
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Adding New Providers
|
|
68
|
+
|
|
69
|
+
When implementing a new LLM provider, follow this systematic approach:
|
|
70
|
+
|
|
71
|
+
### 1. Research Phase
|
|
72
|
+
- **Check ellmer first**: Look in `../ellmer/R/provider-*.R` for existing implementations
|
|
73
|
+
- **Identify base provider**: Most providers inherit from either `OpenAIProvider` (for OpenAI-compatible APIs) or implement `Provider` directly
|
|
74
|
+
- **Check existing patterns**: Review similar providers in `chatlas/_provider_*.py`
|
|
75
|
+
|
|
76
|
+
### 2. Implementation Steps
|
|
77
|
+
1. **Create provider file**: `chatlas/_provider_[name].py`
|
|
78
|
+
- Use PascalCase for class names (e.g., `MistralProvider`)
|
|
79
|
+
- Use snake_case for function names (e.g., `ChatMistral`)
|
|
80
|
+
- Follow existing docstring patterns with Prerequisites, Examples, Parameters, Returns sections
|
|
81
|
+
|
|
82
|
+
2. **Provider class structure**:
|
|
83
|
+
```python
|
|
84
|
+
class [Name]Provider(OpenAIProvider): # or Provider if custom
|
|
85
|
+
def __init__(self, ...):
|
|
86
|
+
super().__init__(...)
|
|
87
|
+
# Provider-specific initialization
|
|
88
|
+
|
|
89
|
+
def _chat_perform_args(self, ...):
|
|
90
|
+
# Customize request parameters if needed
|
|
91
|
+
kwargs = super()._chat_perform_args(...)
|
|
92
|
+
# Apply provider-specific modifications
|
|
93
|
+
return kwargs
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
3. **Chat function signature**:
|
|
97
|
+
```python
|
|
98
|
+
def Chat[Name](
|
|
99
|
+
*,
|
|
100
|
+
system_prompt: Optional[str] = None,
|
|
101
|
+
model: Optional[str] = None,
|
|
102
|
+
api_key: Optional[str] = None,
|
|
103
|
+
base_url: str = "https://...",
|
|
104
|
+
seed: int | None | MISSING_TYPE = MISSING,
|
|
105
|
+
kwargs: Optional["ChatClientArgs"] = None,
|
|
106
|
+
) -> Chat["SubmitInputArgs", ChatCompletion]:
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 3. Testing Setup
|
|
110
|
+
1. **Create test file**: `tests/test_provider_[name].py`
|
|
111
|
+
2. **Add environment variable skip pattern**:
|
|
112
|
+
```python
|
|
113
|
+
import os
|
|
114
|
+
import pytest
|
|
115
|
+
|
|
116
|
+
do_test = os.getenv("TEST_[NAME]", "true")
|
|
117
|
+
if do_test.lower() == "false":
|
|
118
|
+
pytest.skip("Skipping [Name] tests", allow_module_level=True)
|
|
119
|
+
```
|
|
120
|
+
3. **Use standard test patterns**:
|
|
121
|
+
- `test_[name]_simple_request()`
|
|
122
|
+
- `test_[name]_simple_streaming_request()`
|
|
123
|
+
- `test_[name]_respects_turns_interface()`
|
|
124
|
+
- `test_[name]_tool_variations()` (if supported)
|
|
125
|
+
- `test_data_extraction()`
|
|
126
|
+
- `test_[name]_images()` (if vision supported)
|
|
127
|
+
|
|
128
|
+
### 4. Package Integration
|
|
129
|
+
1. **Update `chatlas/__init__.py`**:
|
|
130
|
+
- Add import: `from ._provider_[name] import Chat[Name]`
|
|
131
|
+
- Add to `__all__` tuple: `"Chat[Name]"`
|
|
132
|
+
|
|
133
|
+
2. **Run validation**:
|
|
134
|
+
```bash
|
|
135
|
+
uv run pyright chatlas/_provider_[name].py
|
|
136
|
+
TEST_[NAME]=false uv run pytest tests/test_provider_[name].py -v
|
|
137
|
+
uv run python -c "from chatlas import Chat[Name]; print('Import successful')"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 5. Provider-Specific Customizations
|
|
141
|
+
|
|
142
|
+
**OpenAI-Compatible Providers**:
|
|
143
|
+
- Inherit from `OpenAIProvider`
|
|
144
|
+
- Override `_chat_perform_args()` for API differences
|
|
145
|
+
- Common customizations: remove `stream_options`, adjust parameter names, modify headers
|
|
146
|
+
|
|
147
|
+
**Custom API Providers**:
|
|
148
|
+
- Inherit from `Provider` directly
|
|
149
|
+
- Implement all abstract methods: `chat_perform()`, `chat_perform_async()`, `stream_text()`, etc.
|
|
150
|
+
- Handle model-specific response formats
|
|
151
|
+
|
|
152
|
+
### 6. Common Patterns
|
|
153
|
+
- **Environment variables**: Use `[PROVIDER]_API_KEY` format
|
|
154
|
+
- **Default models**: Use provider's recommended general-purpose model
|
|
155
|
+
- **Seed handling**: `seed = 1014 if is_testing() else None` when MISSING
|
|
156
|
+
- **Error handling**: Provider APIs often return different error formats
|
|
157
|
+
- **Rate limiting**: Consider implementing client-side throttling for providers that need it
|
|
158
|
+
|
|
159
|
+
### 7. Documentation Requirements
|
|
160
|
+
- Include provider description and prerequisites
|
|
161
|
+
- Document known limitations (tool calling, vision support, etc.)
|
|
162
|
+
- Provide working examples with environment variable usage
|
|
163
|
+
- Note any special model requirements (e.g., vision models for images)
|
|
164
|
+
|
|
165
|
+
## Connections to ellmer
|
|
166
|
+
|
|
167
|
+
This project is the Python equivalent of the R package ellmer. The source code for ellmer is available in a sibling directory to this project. Before implementing new features or bug fixes in chatlas, it may be useful to consult the ellmer codebase to: (1) check whether the feature/fix already exists on the R side and (2) make sure the projects are aligned in terms of stylistic approaches. Note also that ellmer itself has a CLAUDE.md file which has a useful overview of the project.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatlas
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: A simple and consistent interface for chatting with LLMs
|
|
5
5
|
Project-URL: Homepage, https://posit-dev.github.io/chatlas
|
|
6
6
|
Project-URL: Documentation, https://posit-dev.github.io/chatlas
|
|
@@ -69,7 +69,7 @@ Provides-Extra: ollama
|
|
|
69
69
|
Provides-Extra: openai
|
|
70
70
|
Provides-Extra: perplexity
|
|
71
71
|
Provides-Extra: snowflake
|
|
72
|
-
Requires-Dist: snowflake-ml-python; extra == 'snowflake'
|
|
72
|
+
Requires-Dist: snowflake-ml-python<=1.9.0; extra == 'snowflake'
|
|
73
73
|
Provides-Extra: test
|
|
74
74
|
Requires-Dist: pyright>=1.1.379; extra == 'test'
|
|
75
75
|
Requires-Dist: pytest-asyncio; extra == 'test'
|
|
@@ -79,7 +79,7 @@ Provides-Extra: vertex
|
|
|
79
79
|
Requires-Dist: google-genai>=1.14.0; extra == 'vertex'
|
|
80
80
|
Description-Content-Type: text/markdown
|
|
81
81
|
|
|
82
|
-
# chatlas <a href="https://posit-dev.github.io/chatlas"><img src="
|
|
82
|
+
# chatlas <a href="https://posit-dev.github.io/chatlas"><img src="https://posit-dev.github.io/chatlas/logos/hex/logo.png" align="right" height="138" alt="chatlas website" /></a>
|
|
83
83
|
|
|
84
84
|
<p>
|
|
85
85
|
<!-- badges start -->
|
|
@@ -135,7 +135,7 @@ chat.chat("How's the weather in San Francisco?")
|
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
|
|
138
|
-
<img src="
|
|
138
|
+
<img src="https://posit-dev.github.io/chatlas/images/chatlas-hello.png" alt="Model response output to the user query: 'How's the weather in San Francisco?'" width="67%" style="display: block; margin-left: auto; margin-right: auto">
|
|
139
139
|
|
|
140
140
|
|
|
141
141
|
Learn more at <https://posit-dev.github.io/chatlas>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# chatlas <a href="https://posit-dev.github.io/chatlas"><img src="
|
|
1
|
+
# chatlas <a href="https://posit-dev.github.io/chatlas"><img src="https://posit-dev.github.io/chatlas/logos/hex/logo.png" align="right" height="138" alt="chatlas website" /></a>
|
|
2
2
|
|
|
3
3
|
<p>
|
|
4
4
|
<!-- badges start -->
|
|
@@ -54,7 +54,7 @@ chat.chat("How's the weather in San Francisco?")
|
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
<img src="
|
|
57
|
+
<img src="https://posit-dev.github.io/chatlas/images/chatlas-hello.png" alt="Model response output to the user query: 'How's the weather in San Francisco?'" width="67%" style="display: block; margin-left: auto; margin-right: auto">
|
|
58
58
|
|
|
59
59
|
|
|
60
60
|
Learn more at <https://posit-dev.github.io/chatlas>
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
from . import types
|
|
2
|
-
from ._anthropic import ChatAnthropic, ChatBedrockAnthropic
|
|
3
2
|
from ._auto import ChatAuto
|
|
4
3
|
from ._chat import Chat
|
|
5
4
|
from ._content import ContentToolRequest, ContentToolResult, ContentToolResultImage
|
|
6
5
|
from ._content_image import content_image_file, content_image_plot, content_image_url
|
|
7
6
|
from ._content_pdf import content_pdf_file, content_pdf_url
|
|
8
|
-
from ._databricks import ChatDatabricks
|
|
9
|
-
from ._github import ChatGithub
|
|
10
|
-
from ._google import ChatGoogle, ChatVertex
|
|
11
|
-
from ._groq import ChatGroq
|
|
12
7
|
from ._interpolate import interpolate, interpolate_file
|
|
13
|
-
from ._ollama import ChatOllama
|
|
14
|
-
from ._openai import ChatAzureOpenAI, ChatOpenAI
|
|
15
|
-
from ._perplexity import ChatPerplexity
|
|
16
8
|
from ._provider import Provider
|
|
17
|
-
from .
|
|
9
|
+
from ._provider_anthropic import ChatAnthropic, ChatBedrockAnthropic
|
|
10
|
+
from ._provider_cloudflare import ChatCloudflare
|
|
11
|
+
from ._provider_databricks import ChatDatabricks
|
|
12
|
+
from ._provider_deepseek import ChatDeepSeek
|
|
13
|
+
from ._provider_github import ChatGithub
|
|
14
|
+
from ._provider_google import ChatGoogle, ChatVertex
|
|
15
|
+
from ._provider_groq import ChatGroq
|
|
16
|
+
from ._provider_huggingface import ChatHuggingFace
|
|
17
|
+
from ._provider_mistral import ChatMistral
|
|
18
|
+
from ._provider_ollama import ChatOllama
|
|
19
|
+
from ._provider_openai import ChatAzureOpenAI, ChatOpenAI
|
|
20
|
+
from ._provider_openrouter import ChatOpenRouter
|
|
21
|
+
from ._provider_perplexity import ChatPerplexity
|
|
22
|
+
from ._provider_portkey import ChatPortkey
|
|
23
|
+
from ._provider_snowflake import ChatSnowflake
|
|
18
24
|
from ._tokens import token_usage
|
|
19
25
|
from ._tools import Tool, ToolRejectError
|
|
20
26
|
from ._turn import Turn
|
|
@@ -28,14 +34,20 @@ __all__ = (
|
|
|
28
34
|
"ChatAnthropic",
|
|
29
35
|
"ChatAuto",
|
|
30
36
|
"ChatBedrockAnthropic",
|
|
37
|
+
"ChatCloudflare",
|
|
31
38
|
"ChatDatabricks",
|
|
39
|
+
"ChatDeepSeek",
|
|
32
40
|
"ChatGithub",
|
|
33
41
|
"ChatGoogle",
|
|
34
42
|
"ChatGroq",
|
|
43
|
+
"ChatHuggingFace",
|
|
44
|
+
"ChatMistral",
|
|
35
45
|
"ChatOllama",
|
|
36
46
|
"ChatOpenAI",
|
|
47
|
+
"ChatOpenRouter",
|
|
37
48
|
"ChatAzureOpenAI",
|
|
38
49
|
"ChatPerplexity",
|
|
50
|
+
"ChatPortkey",
|
|
39
51
|
"ChatSnowflake",
|
|
40
52
|
"ChatVertex",
|
|
41
53
|
"Chat",
|
|
@@ -5,16 +5,16 @@ from typing import Callable, Literal, Optional
|
|
|
5
5
|
|
|
6
6
|
import orjson
|
|
7
7
|
|
|
8
|
-
from ._anthropic import ChatAnthropic, ChatBedrockAnthropic
|
|
9
8
|
from ._chat import Chat
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from .
|
|
13
|
-
from .
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
9
|
+
from ._provider_anthropic import ChatAnthropic, ChatBedrockAnthropic
|
|
10
|
+
from ._provider_databricks import ChatDatabricks
|
|
11
|
+
from ._provider_github import ChatGithub
|
|
12
|
+
from ._provider_google import ChatGoogle, ChatVertex
|
|
13
|
+
from ._provider_groq import ChatGroq
|
|
14
|
+
from ._provider_ollama import ChatOllama
|
|
15
|
+
from ._provider_openai import ChatAzureOpenAI, ChatOpenAI
|
|
16
|
+
from ._provider_perplexity import ChatPerplexity
|
|
17
|
+
from ._provider_snowflake import ChatSnowflake
|
|
18
18
|
|
|
19
19
|
AutoProviders = Literal[
|
|
20
20
|
"anthropic",
|
|
@@ -65,6 +65,7 @@ class TokensDict(TypedDict):
|
|
|
65
65
|
role: Literal["user", "assistant"]
|
|
66
66
|
tokens: int
|
|
67
67
|
tokens_total: int
|
|
68
|
+
tokens_cached: int
|
|
68
69
|
|
|
69
70
|
|
|
70
71
|
CompletionT = TypeVar("CompletionT")
|
|
@@ -293,12 +294,15 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
293
294
|
{
|
|
294
295
|
"role": "user",
|
|
295
296
|
"tokens": turns[1].tokens[0],
|
|
297
|
+
# Number of tokens currently cached (reduces input token usage)
|
|
298
|
+
"tokens_cached": turns[1].tokens[2],
|
|
296
299
|
"tokens_total": turns[1].tokens[0],
|
|
297
300
|
},
|
|
298
301
|
# The token count for the 1st assistant response
|
|
299
302
|
{
|
|
300
303
|
"role": "assistant",
|
|
301
304
|
"tokens": turns[1].tokens[1],
|
|
305
|
+
"tokens_cached": 0,
|
|
302
306
|
"tokens_total": turns[1].tokens[1],
|
|
303
307
|
},
|
|
304
308
|
]
|
|
@@ -319,8 +323,11 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
319
323
|
[
|
|
320
324
|
{
|
|
321
325
|
"role": "user",
|
|
322
|
-
# Implied token count for the user input
|
|
326
|
+
# Implied new token count for the user input (input tokens - context - cached reads)
|
|
327
|
+
# Cached reads are only subtracted for particular providers
|
|
323
328
|
"tokens": tj.tokens[0] - sum(ti.tokens),
|
|
329
|
+
# Number of tokens currently cached (reduces input token usage depending on provider's API)
|
|
330
|
+
"tokens_cached": tj.tokens[2],
|
|
324
331
|
# Total tokens = Total User Tokens for the Turn = Distinct new tokens + context sent
|
|
325
332
|
"tokens_total": tj.tokens[0],
|
|
326
333
|
},
|
|
@@ -329,6 +336,7 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
329
336
|
# The token count for the assistant response
|
|
330
337
|
"tokens": tj.tokens[1],
|
|
331
338
|
# Total tokens = Total Assistant tokens used in the turn
|
|
339
|
+
"tokens_cached": 0,
|
|
332
340
|
"tokens_total": tj.tokens[1],
|
|
333
341
|
},
|
|
334
342
|
]
|
|
@@ -339,7 +347,7 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
339
347
|
def get_cost(
|
|
340
348
|
self,
|
|
341
349
|
options: Literal["all", "last"] = "all",
|
|
342
|
-
token_price: Optional[tuple[float, float]] = None,
|
|
350
|
+
token_price: Optional[tuple[float, float, float]] = None,
|
|
343
351
|
) -> float:
|
|
344
352
|
"""
|
|
345
353
|
Estimate the cost of the chat.
|
|
@@ -357,11 +365,13 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
357
365
|
- `"last"`: Return the cost of the last turn in the chat.
|
|
358
366
|
token_price
|
|
359
367
|
An optional tuple in the format of (input_token_cost,
|
|
360
|
-
output_token_cost) for bringing your own cost information.
|
|
368
|
+
output_token_cost, cached_token_cost) for bringing your own cost information.
|
|
361
369
|
- `"input_token_cost"`: The cost per user token in USD per
|
|
362
370
|
million tokens.
|
|
363
371
|
- `"output_token_cost"`: The cost per assistant token in USD
|
|
364
372
|
per million tokens.
|
|
373
|
+
- `"cached_token_cost"`: The cost per cached token read in USD
|
|
374
|
+
per million tokens.
|
|
365
375
|
|
|
366
376
|
Returns
|
|
367
377
|
-------
|
|
@@ -374,15 +384,19 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
374
384
|
if token_price:
|
|
375
385
|
input_token_price = token_price[0] / 1e6
|
|
376
386
|
output_token_price = token_price[1] / 1e6
|
|
387
|
+
cached_token_price = token_price[2] / 1e6
|
|
377
388
|
else:
|
|
378
389
|
price_token = get_token_pricing(self.provider.name, self.provider.model)
|
|
379
390
|
if not price_token:
|
|
380
391
|
raise KeyError(
|
|
381
|
-
f"We could not locate pricing information for model '{self.provider.model}'
|
|
392
|
+
f"We could not locate pricing information for model '{self.provider.model}'"
|
|
393
|
+
f" from provider '{self.provider.name}'. "
|
|
382
394
|
"If you know the pricing for this model, specify it in `token_price`."
|
|
383
395
|
)
|
|
396
|
+
|
|
384
397
|
input_token_price = price_token["input"] / 1e6
|
|
385
|
-
output_token_price = price_token
|
|
398
|
+
output_token_price = price_token.get("output", 0) / 1e6
|
|
399
|
+
cached_token_price = price_token.get("cached_input", 0) / 1e6
|
|
386
400
|
|
|
387
401
|
if len(turns_tokens) == 0:
|
|
388
402
|
return 0.0
|
|
@@ -399,8 +413,16 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
399
413
|
user_tokens = sum(
|
|
400
414
|
u["tokens_total"] for u in turns_tokens if u["role"] == "user"
|
|
401
415
|
)
|
|
402
|
-
|
|
403
|
-
|
|
416
|
+
# We add the cached tokens here because for relevant providers they have already been subtracted
|
|
417
|
+
# from the user tokens. This assumes the provider uses (reads) the cache each time.
|
|
418
|
+
cached_token_reads = sum(
|
|
419
|
+
u["tokens_cached"] for u in turns_tokens if u["role"] == "user"
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
cost = (
|
|
423
|
+
(asst_tokens * output_token_price)
|
|
424
|
+
+ (user_tokens * input_token_price)
|
|
425
|
+
+ (cached_token_reads * cached_token_price)
|
|
404
426
|
)
|
|
405
427
|
return cost
|
|
406
428
|
|
|
@@ -408,7 +430,9 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
408
430
|
if last_turn["role"] == "assistant":
|
|
409
431
|
return last_turn["tokens"] * output_token_price
|
|
410
432
|
if last_turn["role"] == "user":
|
|
411
|
-
return last_turn["tokens_total"] * input_token_price
|
|
433
|
+
return (last_turn["tokens_total"] * input_token_price) + (
|
|
434
|
+
last_turn["tokens_cached"] * cached_token_price
|
|
435
|
+
)
|
|
412
436
|
raise ValueError(
|
|
413
437
|
f"Expected last turn to have a role of 'user' or `'assistant'`, not '{last_turn['role']}'"
|
|
414
438
|
)
|
|
@@ -2224,8 +2248,12 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
2224
2248
|
tokens = self.get_tokens()
|
|
2225
2249
|
tokens_asst = sum(u["tokens_total"] for u in tokens if u["role"] == "assistant")
|
|
2226
2250
|
tokens_user = sum(u["tokens_total"] for u in tokens if u["role"] == "user")
|
|
2251
|
+
tokens_cached = sum(u["tokens_cached"] for u in tokens if u["role"] == "user")
|
|
2227
2252
|
|
|
2228
|
-
res =
|
|
2253
|
+
res = (
|
|
2254
|
+
f"<Chat {self.provider.name}/{self.provider.model} turns={len(turns)}"
|
|
2255
|
+
f" tokens={tokens_user + tokens_cached}/{tokens_asst}"
|
|
2256
|
+
)
|
|
2229
2257
|
|
|
2230
2258
|
# Add cost info only if we can compute it
|
|
2231
2259
|
cost = compute_cost(
|
|
@@ -2233,6 +2261,7 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
|
|
|
2233
2261
|
self.provider.model,
|
|
2234
2262
|
tokens_user,
|
|
2235
2263
|
tokens_asst,
|
|
2264
|
+
tokens_cached,
|
|
2236
2265
|
)
|
|
2237
2266
|
if cost is not None:
|
|
2238
2267
|
res += f" ${round(cost, ndigits=2)}"
|
|
@@ -163,7 +163,7 @@ def ChatAnthropic(
|
|
|
163
163
|
"""
|
|
164
164
|
|
|
165
165
|
if model is None:
|
|
166
|
-
model = log_model_default("claude-
|
|
166
|
+
model = log_model_default("claude-sonnet-4-0")
|
|
167
167
|
|
|
168
168
|
return Chat(
|
|
169
169
|
provider=AnthropicProvider(
|
|
@@ -586,7 +586,16 @@ class AnthropicProvider(
|
|
|
586
586
|
)
|
|
587
587
|
)
|
|
588
588
|
|
|
589
|
-
|
|
589
|
+
usage = completion.usage
|
|
590
|
+
# N.B. Currently, Anthropic doesn't cache by default and we currently do not support
|
|
591
|
+
# manual caching in chatlas. Note also that this only tracks reads, NOT writes, which
|
|
592
|
+
# have their own cost. To track that properly, we would need another caching category and per-token cost.
|
|
593
|
+
|
|
594
|
+
tokens = (
|
|
595
|
+
completion.usage.input_tokens,
|
|
596
|
+
completion.usage.output_tokens,
|
|
597
|
+
usage.cache_read_input_tokens if usage.cache_read_input_tokens else 0,
|
|
598
|
+
)
|
|
590
599
|
|
|
591
600
|
tokens_log(self, tokens)
|
|
592
601
|
|
|
@@ -733,8 +742,7 @@ def ChatBedrockAnthropic(
|
|
|
733
742
|
"""
|
|
734
743
|
|
|
735
744
|
if model is None:
|
|
736
|
-
|
|
737
|
-
model = log_model_default("anthropic.claude-3-5-sonnet-20241022-v2:0")
|
|
745
|
+
model = log_model_default("us.anthropic.claude-sonnet-4-20250514-v1:0")
|
|
738
746
|
|
|
739
747
|
return Chat(
|
|
740
748
|
provider=AnthropicBedrockProvider(
|
|
@@ -764,7 +772,7 @@ class AnthropicBedrockProvider(AnthropicProvider):
|
|
|
764
772
|
aws_session_token: str | None,
|
|
765
773
|
max_tokens: int = 4096,
|
|
766
774
|
base_url: str | None,
|
|
767
|
-
name: str = "
|
|
775
|
+
name: str = "AWS/Bedrock",
|
|
768
776
|
kwargs: Optional["ChatBedrockClientArgs"] = None,
|
|
769
777
|
):
|
|
770
778
|
super().__init__(name=name, model=model, max_tokens=max_tokens)
|