chatlas 0.2.0__py3-none-any.whl

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/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ from . import types
2
+ from ._anthropic import ChatAnthropic, ChatBedrockAnthropic
3
+ from ._chat import Chat
4
+ from ._content_image import content_image_file, content_image_plot, content_image_url
5
+ from ._github import ChatGithub
6
+ from ._google import ChatGoogle
7
+ from ._groq import ChatGroq
8
+ from ._interpolate import interpolate, interpolate_file
9
+ from ._ollama import ChatOllama
10
+ from ._openai import ChatAzureOpenAI, ChatOpenAI
11
+ from ._perplexity import ChatPerplexity
12
+ from ._provider import Provider
13
+ from ._tokens import token_usage
14
+ from ._tools import Tool
15
+ from ._turn import Turn
16
+
17
+ __all__ = (
18
+ "ChatAnthropic",
19
+ "ChatBedrockAnthropic",
20
+ "ChatGithub",
21
+ "ChatGoogle",
22
+ "ChatGroq",
23
+ "ChatOllama",
24
+ "ChatOpenAI",
25
+ "ChatAzureOpenAI",
26
+ "ChatPerplexity",
27
+ "Chat",
28
+ "content_image_file",
29
+ "content_image_plot",
30
+ "content_image_url",
31
+ "interpolate",
32
+ "interpolate_file",
33
+ "Provider",
34
+ "token_usage",
35
+ "Tool",
36
+ "Turn",
37
+ "types",
38
+ )
chatlas/_anthropic.py ADDED
@@ -0,0 +1,643 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import warnings
5
+ from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast, overload
6
+
7
+ from pydantic import BaseModel
8
+
9
+ from ._chat import Chat
10
+ from ._content import (
11
+ Content,
12
+ ContentImageInline,
13
+ ContentImageRemote,
14
+ ContentJson,
15
+ ContentText,
16
+ ContentToolRequest,
17
+ ContentToolResult,
18
+ )
19
+ from ._logging import log_model_default
20
+ from ._provider import Provider
21
+ from ._tokens import tokens_log
22
+ from ._tools import Tool, basemodel_to_param_schema
23
+ from ._turn import Turn, normalize_turns
24
+
25
+ if TYPE_CHECKING:
26
+ from anthropic.types import (
27
+ Message,
28
+ MessageParam,
29
+ RawMessageStreamEvent,
30
+ TextBlock,
31
+ ToolParam,
32
+ ToolUseBlock,
33
+ )
34
+ from anthropic.types.image_block_param import ImageBlockParam
35
+ from anthropic.types.model_param import ModelParam
36
+ from anthropic.types.text_block_param import TextBlockParam
37
+ from anthropic.types.tool_result_block_param import ToolResultBlockParam
38
+ from anthropic.types.tool_use_block_param import ToolUseBlockParam
39
+ from openai.types.chat import ChatCompletionToolParam
40
+
41
+ from .types.anthropic import ChatBedrockClientArgs, ChatClientArgs, SubmitInputArgs
42
+
43
+ ContentBlockParam = Union[
44
+ TextBlockParam,
45
+ ImageBlockParam,
46
+ ToolUseBlockParam,
47
+ ToolResultBlockParam,
48
+ ]
49
+ else:
50
+ Message = object
51
+ RawMessageStreamEvent = object
52
+
53
+
54
+ def ChatAnthropic(
55
+ *,
56
+ system_prompt: Optional[str] = None,
57
+ turns: Optional[list[Turn]] = None,
58
+ model: "Optional[ModelParam]" = None,
59
+ api_key: Optional[str] = None,
60
+ max_tokens: int = 4096,
61
+ kwargs: Optional["ChatClientArgs"] = None,
62
+ ) -> Chat["SubmitInputArgs", Message]:
63
+ """
64
+ Chat with an Anthropic Claude model.
65
+
66
+ [Anthropic](https://www.anthropic.com) provides a number of chat based
67
+ models under the [Claude](https://www.anthropic.com/claude) moniker.
68
+
69
+ Prerequisites
70
+ -------------
71
+
72
+ ::: {.callout-note}
73
+ ## API key
74
+
75
+ Note that a Claude Prop membership does not give you the ability to call
76
+ models via the API. You will need to go to the [developer
77
+ console](https://console.anthropic.com/account/keys) to sign up (and pay
78
+ for) a developer account that will give you an API key that you can use with
79
+ this package.
80
+ :::
81
+
82
+ ::: {.callout-note}
83
+ ## Python requirements
84
+
85
+ `ChatAnthropic` requires the `anthropic` package (e.g., `pip install anthropic`).
86
+ :::
87
+
88
+ Examples
89
+ --------
90
+
91
+ ```python
92
+ import os
93
+ from chatlas import ChatAnthropic
94
+
95
+ chat = ChatAnthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
96
+ chat.chat("What is the capital of France?")
97
+ ```
98
+
99
+ Parameters
100
+ ----------
101
+ system_prompt
102
+ A system prompt to set the behavior of the assistant.
103
+ turns
104
+ A list of turns to start the chat with (i.e., continuing a previous
105
+ conversation). If not provided, the conversation begins from scratch. Do
106
+ not provide non-None values for both `turns` and `system_prompt`. Each
107
+ message in the list should be a dictionary with at least `role` (usually
108
+ `system`, `user`, or `assistant`, but `tool` is also possible). Normally
109
+ there is also a `content` field, which is a string.
110
+ model
111
+ The model to use for the chat. The default, None, will pick a reasonable
112
+ default, and warn you about it. We strongly recommend explicitly
113
+ choosing a model for all but the most casual use.
114
+ api_key
115
+ The API key to use for authentication. You generally should not supply
116
+ this directly, but instead set the `ANTHROPIC_API_KEY` environment
117
+ variable.
118
+ max_tokens
119
+ Maximum number of tokens to generate before stopping.
120
+ kwargs
121
+ Additional arguments to pass to the `anthropic.Anthropic()` client
122
+ constructor.
123
+
124
+ Returns
125
+ -------
126
+ Chat
127
+ A Chat object.
128
+
129
+ Note
130
+ ----
131
+ Pasting an API key into a chat constructor (e.g., `ChatAnthropic(api_key="...")`)
132
+ is the simplest way to get started, and is fine for interactive use, but is
133
+ problematic for code that may be shared with others.
134
+
135
+ Instead, consider using environment variables or a configuration file to manage
136
+ your credentials. One popular way to manage credentials is to use a `.env` file
137
+ to store your credentials, and then use the `python-dotenv` package to load them
138
+ into your environment.
139
+
140
+ ```shell
141
+ pip install python-dotenv
142
+ ```
143
+
144
+ ```shell
145
+ # .env
146
+ ANTHROPIC_API_KEY=...
147
+ ```
148
+
149
+ ```python
150
+ from chatlas import ChatAnthropic
151
+ from dotenv import load_dotenv
152
+
153
+ load_dotenv()
154
+ chat = ChatAnthropic()
155
+ chat.console()
156
+ ```
157
+
158
+ Another, more general, solution is to load your environment variables into the shell
159
+ before starting Python (maybe in a `.bashrc`, `.zshrc`, etc. file):
160
+
161
+ ```shell
162
+ export ANTHROPIC_API_KEY=...
163
+ ```
164
+ """
165
+
166
+ if model is None:
167
+ model = log_model_default("claude-3-5-sonnet-latest")
168
+
169
+ return Chat(
170
+ provider=AnthropicProvider(
171
+ api_key=api_key,
172
+ model=model,
173
+ max_tokens=max_tokens,
174
+ kwargs=kwargs,
175
+ ),
176
+ turns=normalize_turns(
177
+ turns or [],
178
+ system_prompt,
179
+ ),
180
+ )
181
+
182
+
183
+ class AnthropicProvider(Provider[Message, RawMessageStreamEvent, Message]):
184
+ def __init__(
185
+ self,
186
+ *,
187
+ max_tokens: int,
188
+ model: str,
189
+ api_key: str | None,
190
+ kwargs: Optional["ChatClientArgs"] = None,
191
+ ):
192
+ try:
193
+ from anthropic import Anthropic, AsyncAnthropic
194
+ except ImportError:
195
+ raise ImportError(
196
+ "`ChatAnthropic()` requires the `anthropic` package. "
197
+ "You can install it with 'pip install anthropic'."
198
+ )
199
+
200
+ self._model = model
201
+ self._max_tokens = max_tokens
202
+
203
+ kwargs_full: "ChatClientArgs" = {
204
+ "api_key": api_key,
205
+ **(kwargs or {}),
206
+ }
207
+
208
+ # TODO: worth bringing in sync types?
209
+ self._client = Anthropic(**kwargs_full) # type: ignore
210
+ self._async_client = AsyncAnthropic(**kwargs_full)
211
+
212
+ @overload
213
+ def chat_perform(
214
+ self,
215
+ *,
216
+ stream: Literal[False],
217
+ turns: list[Turn],
218
+ tools: dict[str, Tool],
219
+ data_model: Optional[type[BaseModel]] = None,
220
+ kwargs: Optional["SubmitInputArgs"] = None,
221
+ ): ...
222
+
223
+ @overload
224
+ def chat_perform(
225
+ self,
226
+ *,
227
+ stream: Literal[True],
228
+ turns: list[Turn],
229
+ tools: dict[str, Tool],
230
+ data_model: Optional[type[BaseModel]] = None,
231
+ kwargs: Optional["SubmitInputArgs"] = None,
232
+ ): ...
233
+
234
+ def chat_perform(
235
+ self,
236
+ *,
237
+ stream: bool,
238
+ turns: list[Turn],
239
+ tools: dict[str, Tool],
240
+ data_model: Optional[type[BaseModel]] = None,
241
+ kwargs: Optional["SubmitInputArgs"] = None,
242
+ ):
243
+ kwargs = self._chat_perform_args(stream, turns, tools, data_model, kwargs)
244
+ return self._client.messages.create(**kwargs) # type: ignore
245
+
246
+ @overload
247
+ async def chat_perform_async(
248
+ self,
249
+ *,
250
+ stream: Literal[False],
251
+ turns: list[Turn],
252
+ tools: dict[str, Tool],
253
+ data_model: Optional[type[BaseModel]] = None,
254
+ kwargs: Optional["SubmitInputArgs"] = None,
255
+ ): ...
256
+
257
+ @overload
258
+ async def chat_perform_async(
259
+ self,
260
+ *,
261
+ stream: Literal[True],
262
+ turns: list[Turn],
263
+ tools: dict[str, Tool],
264
+ data_model: Optional[type[BaseModel]] = None,
265
+ kwargs: Optional["SubmitInputArgs"] = None,
266
+ ): ...
267
+
268
+ async def chat_perform_async(
269
+ self,
270
+ *,
271
+ stream: bool,
272
+ turns: list[Turn],
273
+ tools: dict[str, Tool],
274
+ data_model: Optional[type[BaseModel]] = None,
275
+ kwargs: Optional["SubmitInputArgs"] = None,
276
+ ):
277
+ kwargs = self._chat_perform_args(stream, turns, tools, data_model, kwargs)
278
+ return await self._async_client.messages.create(**kwargs) # type: ignore
279
+
280
+ def _chat_perform_args(
281
+ self,
282
+ stream: bool,
283
+ turns: list[Turn],
284
+ tools: dict[str, Tool],
285
+ data_model: Optional[type[BaseModel]] = None,
286
+ kwargs: Optional["SubmitInputArgs"] = None,
287
+ ) -> "SubmitInputArgs":
288
+ tool_schemas = [
289
+ self._anthropic_tool_schema(tool.schema) for tool in tools.values()
290
+ ]
291
+
292
+ # If data extraction is requested, add a "mock" tool with parameters inferred from the data model
293
+ data_model_tool: Tool | None = None
294
+ if data_model is not None:
295
+
296
+ def _structured_tool_call(**kwargs: Any):
297
+ """Extract structured data"""
298
+ pass
299
+
300
+ data_model_tool = Tool(_structured_tool_call)
301
+
302
+ data_model_tool.schema["function"]["parameters"] = {
303
+ "type": "object",
304
+ "properties": {
305
+ "data": basemodel_to_param_schema(data_model),
306
+ },
307
+ }
308
+
309
+ tool_schemas.append(self._anthropic_tool_schema(data_model_tool.schema))
310
+
311
+ if stream:
312
+ stream = False
313
+ warnings.warn(
314
+ "Anthropic does not support structured data extraction in streaming mode."
315
+ )
316
+
317
+ kwargs_full: "SubmitInputArgs" = {
318
+ "stream": stream,
319
+ "messages": self._as_message_params(turns),
320
+ "model": self._model,
321
+ "max_tokens": self._max_tokens,
322
+ "tools": tool_schemas,
323
+ **(kwargs or {}),
324
+ }
325
+
326
+ if data_model_tool:
327
+ kwargs_full["tool_choice"] = {
328
+ "type": "tool",
329
+ "name": data_model_tool.name,
330
+ }
331
+
332
+ if "system" not in kwargs_full:
333
+ if len(turns) > 0 and turns[0].role == "system":
334
+ kwargs_full["system"] = turns[0].text
335
+
336
+ return kwargs_full
337
+
338
+ def stream_text(self, chunk) -> Optional[str]:
339
+ if chunk.type == "content_block_delta" and chunk.delta.type == "text_delta":
340
+ return chunk.delta.text
341
+ return None
342
+
343
+ def stream_merge_chunks(self, completion, chunk):
344
+ if chunk.type == "message_start":
345
+ return chunk.message
346
+ completion = cast("Message", completion)
347
+
348
+ if chunk.type == "content_block_start":
349
+ completion.content.append(chunk.content_block)
350
+ elif chunk.type == "content_block_delta":
351
+ this_content = completion.content[chunk.index]
352
+ if chunk.delta.type == "text_delta":
353
+ this_content = cast("TextBlock", this_content)
354
+ this_content.text += chunk.delta.text
355
+ elif chunk.delta.type == "input_json_delta":
356
+ this_content = cast("ToolUseBlock", this_content)
357
+ if not isinstance(this_content.input, str):
358
+ this_content.input = ""
359
+ this_content.input += chunk.delta.partial_json
360
+ elif chunk.type == "content_block_stop":
361
+ this_content = completion.content[chunk.index]
362
+ if this_content.type == "tool_use" and isinstance(this_content.input, str):
363
+ try:
364
+ this_content.input = json.loads(this_content.input or "{}")
365
+ except json.JSONDecodeError as e:
366
+ raise ValueError(f"Invalid JSON input: {e}")
367
+ elif chunk.type == "message_delta":
368
+ completion.stop_reason = chunk.delta.stop_reason
369
+ completion.stop_sequence = chunk.delta.stop_sequence
370
+ completion.usage.output_tokens = chunk.usage.output_tokens
371
+
372
+ return completion
373
+
374
+ def stream_turn(self, completion, has_data_model, stream) -> Turn:
375
+ return self._as_turn(completion, has_data_model)
376
+
377
+ async def stream_turn_async(self, completion, has_data_model, stream) -> Turn:
378
+ return self._as_turn(completion, has_data_model)
379
+
380
+ def value_turn(self, completion, has_data_model) -> Turn:
381
+ return self._as_turn(completion, has_data_model)
382
+
383
+ def _as_message_params(self, turns: list[Turn]) -> list["MessageParam"]:
384
+ messages: list["MessageParam"] = []
385
+ for turn in turns:
386
+ if turn.role == "system":
387
+ continue # system prompt passed as separate arg
388
+ if turn.role not in ["user", "assistant"]:
389
+ raise ValueError(f"Unknown role {turn.role}")
390
+
391
+ content = [self._as_content_block(c) for c in turn.contents]
392
+ role = "user" if turn.role == "user" else "assistant"
393
+ messages.append({"role": role, "content": content})
394
+ return messages
395
+
396
+ @staticmethod
397
+ def _as_content_block(content: Content) -> "ContentBlockParam":
398
+ if isinstance(content, ContentText):
399
+ return {"text": content.text, "type": "text"}
400
+ elif isinstance(content, ContentJson):
401
+ return {"text": "<structured data/>", "type": "text"}
402
+ elif isinstance(content, ContentImageInline):
403
+ return {
404
+ "type": "image",
405
+ "source": {
406
+ "type": "base64",
407
+ "media_type": content.content_type,
408
+ "data": content.data or "",
409
+ },
410
+ }
411
+ elif isinstance(content, ContentImageRemote):
412
+ raise NotImplementedError(
413
+ "Remote images aren't supported by Anthropic (Claude). "
414
+ "Consider downloading the image and using content_image_file() instead."
415
+ )
416
+ elif isinstance(content, ContentToolRequest):
417
+ return {
418
+ "type": "tool_use",
419
+ "id": content.id,
420
+ "name": content.name,
421
+ "input": content.arguments,
422
+ }
423
+ elif isinstance(content, ContentToolResult):
424
+ return {
425
+ "type": "tool_result",
426
+ "tool_use_id": content.id,
427
+ "content": content.get_final_value(),
428
+ "is_error": content.error is not None,
429
+ }
430
+ raise ValueError(f"Unknown content type: {type(content)}")
431
+
432
+ @staticmethod
433
+ def _anthropic_tool_schema(schema: "ChatCompletionToolParam") -> "ToolParam":
434
+ fn = schema["function"]
435
+ name = fn["name"]
436
+
437
+ res: "ToolParam" = {
438
+ "name": name,
439
+ "input_schema": {
440
+ "type": "object",
441
+ },
442
+ }
443
+
444
+ if "description" in fn:
445
+ res["description"] = fn["description"]
446
+
447
+ if "parameters" in fn:
448
+ res["input_schema"]["properties"] = fn["parameters"]["properties"]
449
+
450
+ return res
451
+
452
+ def _as_turn(self, completion: Message, has_data_model=False) -> Turn:
453
+ contents = []
454
+ for content in completion.content:
455
+ if content.type == "text":
456
+ contents.append(ContentText(content.text))
457
+ elif content.type == "tool_use":
458
+ if has_data_model and content.name == "_structured_tool_call":
459
+ if not isinstance(content.input, dict):
460
+ raise ValueError(
461
+ "Expected data extraction tool to return a dictionary."
462
+ )
463
+ if "data" not in content.input:
464
+ raise ValueError(
465
+ "Expected data extraction tool to return a 'data' field."
466
+ )
467
+ contents.append(ContentJson(content.input["data"]))
468
+ else:
469
+ contents.append(
470
+ ContentToolRequest(
471
+ content.id,
472
+ name=content.name,
473
+ arguments=content.input,
474
+ )
475
+ )
476
+
477
+ tokens = completion.usage.input_tokens, completion.usage.output_tokens
478
+
479
+ tokens_log(self, tokens)
480
+
481
+ return Turn(
482
+ "assistant",
483
+ contents,
484
+ tokens=tokens,
485
+ finish_reason=completion.stop_reason,
486
+ completion=completion,
487
+ )
488
+
489
+
490
+ def ChatBedrockAnthropic(
491
+ *,
492
+ model: Optional[str] = None,
493
+ max_tokens: int = 4096,
494
+ aws_secret_key: Optional[str] = None,
495
+ aws_access_key: Optional[str] = None,
496
+ aws_region: Optional[str] = None,
497
+ aws_profile: Optional[str] = None,
498
+ aws_session_token: Optional[str] = None,
499
+ base_url: Optional[str] = None,
500
+ system_prompt: Optional[str] = None,
501
+ turns: Optional[list[Turn]] = None,
502
+ kwargs: Optional["ChatBedrockClientArgs"] = None,
503
+ ) -> Chat["SubmitInputArgs", Message]:
504
+ """
505
+ Chat with an AWS bedrock model.
506
+
507
+ [AWS Bedrock](https://aws.amazon.com/bedrock/) provides a number of chat
508
+ based models, including those Anthropic's
509
+ [Claude](https://aws.amazon.com/bedrock/claude/).
510
+
511
+ Prerequisites
512
+ -------------
513
+
514
+ ::: {.callout-note}
515
+ ## AWS credentials
516
+
517
+ Consider using the approach outlined in this guide to manage your AWS credentials:
518
+ <https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html>
519
+ :::
520
+
521
+ ::: {.callout-note}
522
+ ## Python requirements
523
+
524
+ `ChatBedrockAnthropic`, requires the `anthropic` package with the `bedrock` extras
525
+ (e.g., `pip install anthropic[bedrock]`).
526
+ :::
527
+
528
+ Examples
529
+ --------
530
+
531
+ ```python
532
+ from chatlas import ChatBedrockAnthropic
533
+
534
+ chat = ChatBedrockAnthropic(
535
+ aws_profile="...",
536
+ aws_region="us-east",
537
+ aws_secret_key="...",
538
+ aws_access_key="...",
539
+ aws_session_token="...",
540
+ )
541
+ chat.chat("What is the capital of France?")
542
+ ```
543
+
544
+ Parameters
545
+ ----------
546
+ model
547
+ The model to use for the chat.
548
+ max_tokens
549
+ Maximum number of tokens to generate before stopping.
550
+ aws_secret_key
551
+ The AWS secret key to use for authentication.
552
+ aws_access_key
553
+ The AWS access key to use for authentication.
554
+ aws_region
555
+ The AWS region to use. Defaults to the AWS_REGION environment variable.
556
+ If that is not set, defaults to `'us-east-1'`.
557
+ aws_profile
558
+ The AWS profile to use.
559
+ aws_session_token
560
+ The AWS session token to use.
561
+ base_url
562
+ The base URL to use. Defaults to the ANTHROPIC_BEDROCK_BASE_URL
563
+ environment variable. If that is not set, defaults to
564
+ `f"https://bedrock-runtime.{aws_region}.amazonaws.com"`.
565
+ system_prompt
566
+ A system prompt to set the behavior of the assistant.
567
+ turns
568
+ A list of turns to start the chat with (i.e., continuing a previous
569
+ conversation). If not provided, the conversation begins from scratch. Do
570
+ not provide non-None values for both `turns` and `system_prompt`. Each
571
+ message in the list should be a dictionary with at least `role` (usually
572
+ `system`, `user`, or `assistant`, but `tool` is also possible). Normally
573
+ there is also a `content` field, which is a string.
574
+ kwargs
575
+ Additional arguments to pass to the `anthropic.AnthropicBedrock()`
576
+ client constructor.
577
+
578
+ Returns
579
+ -------
580
+ Chat
581
+ A Chat object.
582
+ """
583
+
584
+ if model is None:
585
+ # Default model from https://github.com/anthropics/anthropic-sdk-python?tab=readme-ov-file#aws-bedrock
586
+ model = log_model_default("anthropic.claude-3-5-sonnet-20241022-v2:0")
587
+
588
+ return Chat(
589
+ provider=AnthropicBedrockProvider(
590
+ model=model,
591
+ max_tokens=max_tokens,
592
+ aws_secret_key=aws_secret_key,
593
+ aws_access_key=aws_access_key,
594
+ aws_region=aws_region,
595
+ aws_profile=aws_profile,
596
+ aws_session_token=aws_session_token,
597
+ base_url=base_url,
598
+ kwargs=kwargs,
599
+ ),
600
+ turns=normalize_turns(
601
+ turns or [],
602
+ system_prompt,
603
+ ),
604
+ )
605
+
606
+
607
+ class AnthropicBedrockProvider(AnthropicProvider):
608
+ def __init__(
609
+ self,
610
+ *,
611
+ model: str,
612
+ aws_secret_key: str | None,
613
+ aws_access_key: str | None,
614
+ aws_region: str | None,
615
+ aws_profile: str | None,
616
+ aws_session_token: str | None,
617
+ max_tokens: int,
618
+ base_url: str | None,
619
+ kwargs: Optional["ChatBedrockClientArgs"] = None,
620
+ ):
621
+ try:
622
+ from anthropic import AnthropicBedrock, AsyncAnthropicBedrock
623
+ except ImportError:
624
+ raise ImportError(
625
+ "`ChatBedrockAnthropic()` requires the `anthropic` package. "
626
+ "Install it with `pip install anthropic[bedrock]`."
627
+ )
628
+
629
+ self._model = model
630
+ self._max_tokens = max_tokens
631
+
632
+ kwargs_full: "ChatBedrockClientArgs" = {
633
+ "aws_secret_key": aws_secret_key,
634
+ "aws_access_key": aws_access_key,
635
+ "aws_region": aws_region,
636
+ "aws_profile": aws_profile,
637
+ "aws_session_token": aws_session_token,
638
+ "base_url": base_url,
639
+ **(kwargs or {}),
640
+ }
641
+
642
+ self._client = AnthropicBedrock(**kwargs_full) # type: ignore
643
+ self._async_client = AsyncAnthropicBedrock(**kwargs_full) # type: ignore