From e3557e48cce8b11135579d777d0f6151bea006b2 Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 12 Jun 2026 14:47:52 -0500 Subject: [PATCH 1/6] chore: update anthropic types --- chatlas/types/anthropic/_client.py | 38 +++++++++++++++++++++- chatlas/types/anthropic/_client_bedrock.py | 38 +++++++++++++++++++++- chatlas/types/anthropic/_submit.py | 2 ++ scripts/_generate_anthropic_types.py | 11 +++++-- scripts/_utils.py | 23 +++++++++++++ scripts/demo_citations.py | 16 +++++++++ scripts/test_bedrock_cache.py | 14 ++++++++ 7 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 scripts/demo_citations.py create mode 100644 scripts/test_bedrock_cache.py diff --git a/chatlas/types/anthropic/_client.py b/chatlas/types/anthropic/_client.py index 834ae19c..08e7ed9f 100644 --- a/chatlas/types/anthropic/_client.py +++ b/chatlas/types/anthropic/_client.py @@ -3,9 +3,19 @@ # --------------------------------------------------------- -from typing import Any, Mapping, Optional, TypedDict +from typing import ( + Any, + Awaitable, + Callable, + Mapping, + Optional, + Sequence, + TypedDict, + Union, +) import anthropic +import anthropic._response import anthropic.lib.credentials._cache import anthropic.lib.credentials._types import httpx @@ -24,6 +34,32 @@ class ChatClientArgs(TypedDict, total=False): default_headers: Optional[Mapping[str, str]] default_query: Optional[Mapping[str, object]] http_client: httpx.AsyncClient | None + middleware: Optional[ + Sequence[ + Union[ + anthropic.Middleware, + Callable[ + [ + anthropic.APIRequest, + Callable[ + [anthropic.APIRequest], anthropic._response.APIResponse[Any] + ], + ], + anthropic._response.APIResponse[Any], + ], + Callable[ + [ + anthropic.APIRequest, + Callable[ + [anthropic.APIRequest], + Awaitable[anthropic._response.AsyncAPIResponse[Any]], + ], + ], + Awaitable[anthropic._response.AsyncAPIResponse[Any]], + ], + ] + ] + ] _strict_response_validation: bool _token_cache: ( anthropic.lib.credentials._cache.TokenCache | None | anthropic.NotGiven diff --git a/chatlas/types/anthropic/_client_bedrock.py b/chatlas/types/anthropic/_client_bedrock.py index e1a43dbe..5034a01a 100644 --- a/chatlas/types/anthropic/_client_bedrock.py +++ b/chatlas/types/anthropic/_client_bedrock.py @@ -2,9 +2,19 @@ # Do not modify this file. It was generated by `scripts/generate_typed_dicts.py`. # --------------------------------------------------------- -from typing import Mapping, Optional, TypedDict +from typing import ( + Any, + Awaitable, + Callable, + Mapping, + Optional, + Sequence, + TypedDict, + Union, +) import anthropic +import anthropic._response import httpx @@ -21,4 +31,30 @@ class ChatBedrockClientArgs(TypedDict, total=False): default_headers: Optional[Mapping[str, str]] default_query: Optional[Mapping[str, object]] http_client: httpx.AsyncClient | None + middleware: Optional[ + Sequence[ + Union[ + anthropic.Middleware, + Callable[ + [ + anthropic.APIRequest, + Callable[ + [anthropic.APIRequest], anthropic._response.APIResponse[Any] + ], + ], + anthropic._response.APIResponse[Any], + ], + Callable[ + [ + anthropic.APIRequest, + Callable[ + [anthropic.APIRequest], + Awaitable[anthropic._response.AsyncAPIResponse[Any]], + ], + ], + Awaitable[anthropic._response.AsyncAPIResponse[Any]], + ], + ] + ] + ] _strict_response_validation: bool diff --git a/chatlas/types/anthropic/_submit.py b/chatlas/types/anthropic/_submit.py index 9e9695f0..b06586b2 100644 --- a/chatlas/types/anthropic/_submit.py +++ b/chatlas/types/anthropic/_submit.py @@ -40,6 +40,8 @@ class SubmitInputArgs(TypedDict, total=False): messages: Iterable[anthropic.types.message_param.MessageParam] model: Union[ Literal[ + "claude-fable-5", + "claude-mythos-5", "claude-opus-4-8", "claude-opus-4-7", "claude-mythos-preview", diff --git a/scripts/_generate_anthropic_types.py b/scripts/_generate_anthropic_types.py index a2e0ff72..91381010 100644 --- a/scripts/_generate_anthropic_types.py +++ b/scripts/_generate_anthropic_types.py @@ -2,6 +2,7 @@ import httpx from anthropic import AsyncAnthropic, AsyncAnthropicBedrock +from anthropic._response import APIResponse, AsyncAPIResponse from anthropic.resources import AsyncMessages from _utils import generate_typeddict_code, write_code_to_file @@ -27,11 +28,17 @@ provider_dir / "_submit.py", ) +_anthropic_localns = { + "URL": httpx.URL, + "APIResponse": APIResponse, + "AsyncAPIResponse": AsyncAPIResponse, +} + init_args = generate_typeddict_code( AsyncAnthropic.__init__, "ChatClientArgs", excluded_fields={"self"}, - localns={"URL": httpx.URL}, + localns=_anthropic_localns, ) write_code_to_file( @@ -44,7 +51,7 @@ AsyncAnthropicBedrock.__init__, "ChatBedrockClientArgs", excluded_fields={"self"}, - localns={"URL": httpx.URL}, + localns=_anthropic_localns, ) write_code_to_file( diff --git a/scripts/_utils.py b/scripts/_utils.py index 17eeac57..ff8de93a 100644 --- a/scripts/_utils.py +++ b/scripts/_utils.py @@ -1,3 +1,4 @@ +import collections.abc import inspect import re import subprocess @@ -111,6 +112,19 @@ def get_type_string(typ: Any) -> tuple[str, set[str]]: args = ", ".join(repr(arg) for arg in typ.__args__) imports.add("from typing import Literal") return f"Literal[{args}]", imports + if origin is collections.abc.Callable: + type_args = typ.__args__ or () + param_strs = [] + for arg in type_args[:-1]: + arg_str, arg_imports = get_type_string(arg) + param_strs.append(arg_str) + imports.update(arg_imports) + ret_str, ret_imports = ( + get_type_string(type_args[-1]) if type_args else ("Any", set()) + ) + imports.update(ret_imports) + imports.add("from typing import Callable") + return f"Callable[[{', '.join(param_strs)}], {ret_str}]", imports origin_name = origin.__name__ args = [] for arg in typ.__args__: @@ -119,6 +133,15 @@ def get_type_string(typ: Any) -> tuple[str, set[str]]: imports.update(arg_imports) if origin_name in TYPING_IMPORTS: imports.add(f"from typing import {origin_name}") + elif getattr(origin, "__module__", "builtins") not in ( + "builtins", + "typing", + "typing_extensions", + "collections.abc", + ): + module = origin.__module__ + imports.add(f"import {module}") + origin_name = f"{module}.{origin_name}" return f"{origin_name}[{', '.join(args)}]", imports elif isinstance(typ, type): module = typ.__module__ diff --git a/scripts/demo_citations.py b/scripts/demo_citations.py new file mode 100644 index 00000000..dd0dff32 --- /dev/null +++ b/scripts/demo_citations.py @@ -0,0 +1,16 @@ +""" +Demo of web search citations — run with: + uv run python scripts/demo_citations.py +""" + +from chatlas import ChatOpenAI, tool_web_search + +chat = ChatOpenAI(model="gpt-4.1") +chat.register_tool(tool_web_search()) +chat.chat("When was ggplot2 first released on CRAN, and who created it?", echo="all") + +print("\n--- Turn contents ---\n") +turn = chat.get_last_turn() +assert turn is not None +for c in turn.contents: + print(c) diff --git a/scripts/test_bedrock_cache.py b/scripts/test_bedrock_cache.py new file mode 100644 index 00000000..f08491ec --- /dev/null +++ b/scripts/test_bedrock_cache.py @@ -0,0 +1,14 @@ +""" +Quick test: can ChatBedrockAnthropic work with non-Anthropic models? +""" + +from chatlas import ChatBedrockAnthropic + +for model in ["amazon.nova-lite-v1:0", "us.amazon.nova-lite-v1:0", "meta.llama3-1-8b-instruct-v1:0"]: + print(f"\n--- Testing model: {model} ---") + try: + chat = ChatBedrockAnthropic(model=model) + response = chat.chat("What is 1 + 1?") + print(f" Success: {response}") + except Exception as e: + print(f" Error ({type(e).__name__}): {e}") From c34722a33e7b50f046ea3093675e1070027d82be Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 12 Jun 2026 15:18:05 -0500 Subject: [PATCH 2/6] chore: update prices --- chatlas/data/prices.json | 112 ++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/chatlas/data/prices.json b/chatlas/data/prices.json index 148f6913..b7342ce1 100644 --- a/chatlas/data/prices.json +++ b/chatlas/data/prices.json @@ -1140,17 +1140,17 @@ "provider": "AWS/Bedrock", "model": "us-gov-east-1/anthropic.claude-sonnet-4-5-20250929-v1:0", "variant": "", - "input": 3.3, - "output": 16.5, - "cached_input": 0.33 + "input": 3.6, + "output": 18, + "cached_input": 0.36 }, { "provider": "AWS/Bedrock", "model": "us-gov-east-1/claude-sonnet-4-5-20250929-v1:0", "variant": "", - "input": 3.3, - "output": 16.5, - "cached_input": 0.33 + "input": 3.6, + "output": 18, + "cached_input": 0.36 }, { "provider": "AWS/Bedrock", @@ -1244,17 +1244,17 @@ "provider": "AWS/Bedrock", "model": "us-gov-west-1/anthropic.claude-sonnet-4-5-20250929-v1:0", "variant": "", - "input": 3.3, - "output": 16.5, - "cached_input": 0.33 + "input": 3.6, + "output": 18, + "cached_input": 0.36 }, { "provider": "AWS/Bedrock", "model": "us-gov-west-1/claude-sonnet-4-5-20250929-v1:0", "variant": "", - "input": 3.3, - "output": 16.5, - "cached_input": 0.33 + "input": 3.6, + "output": 18, + "cached_input": 0.36 }, { "provider": "AWS/Bedrock", @@ -4271,6 +4271,87 @@ "output": 1.5, "cached_input": 0.025 }, + { + "provider": "Groq", + "model": "gemma-7b-it", + "variant": "", + "input": 0.05, + "output": 0.08 + }, + { + "provider": "Groq", + "model": "llama-3.1-8b-instant", + "variant": "", + "input": 0.05, + "output": 0.08 + }, + { + "provider": "Groq", + "model": "llama-3.3-70b-versatile", + "variant": "", + "input": 0.59, + "output": 0.79 + }, + { + "provider": "Groq", + "model": "meta-llama/llama-4-maverick-17b-128e-instruct", + "variant": "", + "input": 0.2, + "output": 0.6 + }, + { + "provider": "Groq", + "model": "meta-llama/llama-4-scout-17b-16e-instruct", + "variant": "", + "input": 0.11, + "output": 0.34 + }, + { + "provider": "Groq", + "model": "meta-llama/llama-guard-4-12b", + "variant": "", + "input": 0.2, + "output": 0.2 + }, + { + "provider": "Groq", + "model": "moonshotai/kimi-k2-instruct-0905", + "variant": "", + "input": 1, + "output": 3, + "cached_input": 0.5 + }, + { + "provider": "Groq", + "model": "openai/gpt-oss-120b", + "variant": "", + "input": 0.15, + "output": 0.6, + "cached_input": 0.075 + }, + { + "provider": "Groq", + "model": "openai/gpt-oss-20b", + "variant": "", + "input": 0.075, + "output": 0.3, + "cached_input": 0.0375 + }, + { + "provider": "Groq", + "model": "openai/gpt-oss-safeguard-20b", + "variant": "", + "input": 0.075, + "output": 0.3, + "cached_input": 0.037 + }, + { + "provider": "Groq", + "model": "qwen/qwen3-32b", + "variant": "", + "input": 0.29, + "output": 0.59 + }, { "provider": "Mistral", "model": "codestral-2405", @@ -4444,6 +4525,13 @@ "input": 0.15, "output": 0.15 }, + { + "provider": "Mistral", + "model": "ministral-8b-latest", + "variant": "", + "input": 0.15, + "output": 0.15 + }, { "provider": "Mistral", "model": "mistral-embed", From 47c065384a35d589e24c2908123b262744981629 Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 12 Jun 2026 15:19:40 -0500 Subject: [PATCH 3/6] chore: add anthropic version requirement --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 29b3e348..a18ade77 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ dev = [ "pandas", "polars", "openai", - "anthropic[bedrock]", + "anthropic[bedrock]>=0.109.1", "google-genai>=1.14.0", "numpy>1.24.4", "tiktoken", @@ -99,7 +99,7 @@ eval = [ ] # Provider extras ---- anthropic = ["anthropic"] -bedrock-anthropic = ["anthropic[bedrock]"] +bedrock-anthropic = ["anthropic[bedrock]>=0.109.1"] databricks = ["databricks-sdk"] # Intentionally empty since these providers used to require # an additional openai install, but that's now included From b6d5166e433341da27e0ec3292406d45c99fd8de Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 12 Jun 2026 15:26:59 -0500 Subject: [PATCH 4/6] chore: rm file --- scripts/demo_citations.py | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 scripts/demo_citations.py diff --git a/scripts/demo_citations.py b/scripts/demo_citations.py deleted file mode 100644 index dd0dff32..00000000 --- a/scripts/demo_citations.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Demo of web search citations — run with: - uv run python scripts/demo_citations.py -""" - -from chatlas import ChatOpenAI, tool_web_search - -chat = ChatOpenAI(model="gpt-4.1") -chat.register_tool(tool_web_search()) -chat.chat("When was ggplot2 first released on CRAN, and who created it?", echo="all") - -print("\n--- Turn contents ---\n") -turn = chat.get_last_turn() -assert turn is not None -for c in turn.contents: - print(c) From 0cae27e1f3614fd4875feac10d63b08e134b0b45 Mon Sep 17 00:00:00 2001 From: Carson Sievert Date: Fri, 12 Jun 2026 15:39:23 -0500 Subject: [PATCH 5/6] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- scripts/_utils.py | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/scripts/_utils.py b/scripts/_utils.py index ff8de93a..ec92de7d 100644 --- a/scripts/_utils.py +++ b/scripts/_utils.py @@ -114,14 +114,34 @@ def get_type_string(typ: Any) -> tuple[str, set[str]]: return f"Literal[{args}]", imports if origin is collections.abc.Callable: type_args = typ.__args__ or () + + # typing.Callable is typically represented as (Ellipsis, R) or (A, B, R) + # (older versions may use ([A, B], R)). + if not type_args: + imports.add("from typing import Callable") + return "Callable[..., Any]", imports + + ret_type = type_args[-1] + params = type_args[:-1] + + # Handle Callable[..., R] + if len(type_args) == 2 and type_args[0] is Ellipsis: + ret_str, ret_imports = get_type_string(ret_type) + imports.update(ret_imports) + imports.add("from typing import Callable") + return f"Callable[..., {ret_str}]", imports + + # Handle Callable[[A, B], R] + if len(type_args) == 2 and isinstance(type_args[0], (list, tuple)): + params = tuple(type_args[0]) + param_strs = [] - for arg in type_args[:-1]: + for arg in params: arg_str, arg_imports = get_type_string(arg) param_strs.append(arg_str) imports.update(arg_imports) - ret_str, ret_imports = ( - get_type_string(type_args[-1]) if type_args else ("Any", set()) - ) + + ret_str, ret_imports = get_type_string(ret_type) imports.update(ret_imports) imports.add("from typing import Callable") return f"Callable[[{', '.join(param_strs)}], {ret_str}]", imports From 8c01af3fa8303e8d8e01686575ef27dbb94a88ae Mon Sep 17 00:00:00 2001 From: Carson Date: Fri, 12 Jun 2026 15:49:32 -0500 Subject: [PATCH 6/6] Anthropic version requirement and remove unneeded test --- pyproject.toml | 2 +- scripts/test_bedrock_cache.py | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 scripts/test_bedrock_cache.py diff --git a/pyproject.toml b/pyproject.toml index a18ade77..d01e8a48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ eval = [ "inspect-ai;python_version>='3.10'" ] # Provider extras ---- -anthropic = ["anthropic"] +anthropic = ["anthropic>=0.109.1"] bedrock-anthropic = ["anthropic[bedrock]>=0.109.1"] databricks = ["databricks-sdk"] # Intentionally empty since these providers used to require diff --git a/scripts/test_bedrock_cache.py b/scripts/test_bedrock_cache.py deleted file mode 100644 index f08491ec..00000000 --- a/scripts/test_bedrock_cache.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Quick test: can ChatBedrockAnthropic work with non-Anthropic models? -""" - -from chatlas import ChatBedrockAnthropic - -for model in ["amazon.nova-lite-v1:0", "us.amazon.nova-lite-v1:0", "meta.llama3-1-8b-instruct-v1:0"]: - print(f"\n--- Testing model: {model} ---") - try: - chat = ChatBedrockAnthropic(model=model) - response = chat.chat("What is 1 + 1?") - print(f" Success: {response}") - except Exception as e: - print(f" Error ({type(e).__name__}): {e}")