qtype 0.1.13__py3-none-any.whl → 0.1.14__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.
Files changed (42) hide show
  1. qtype/base/__init__.py +8 -2
  2. qtype/base/logging.py +0 -17
  3. qtype/base/resources.py +193 -0
  4. qtype/cli.py +5 -9
  5. qtype/commands/generate.py +6 -1
  6. qtype/commands/run.py +37 -10
  7. qtype/docs/Gallery/dataflow_pipelines.md +15 -2
  8. qtype/docs/Gallery/recipe_chatbot.md +103 -0
  9. qtype/docs/Gallery/recipe_chatbot.mermaid +62 -0
  10. qtype/docs/Gallery/recipe_chatbot.png +0 -0
  11. qtype/docs/Gallery/research_assistant.md +1 -1
  12. qtype/docs/How To/Command Line Usage/pass_inputs_on_the_cli.md +4 -1
  13. qtype/docs/How To/Data Processing/load_documents.md +74 -0
  14. qtype/docs/How To/Data Processing/read_sql_databases.md +2 -0
  15. qtype/docs/Reference/cli.md +3 -2
  16. qtype/docs/Reference/plugins.md +0 -4
  17. qtype/docs/Reference/semantic-validation-rules.md +1 -6
  18. qtype/docs/Tutorials/01-first-qtype-application.md +1 -1
  19. qtype/docs/Tutorials/03-structured-data.md +1 -1
  20. qtype/docs/Tutorials/04-tools-and-function-calling.md +1 -1
  21. qtype/examples/conversational_ai/simple_chatbot_with_auth.qtype.yaml +48 -0
  22. qtype/examples/data_processing/load_documents.qtype.yaml +31 -0
  23. qtype/examples/invoke_models/invoke_embedding_aws.qtype.yaml +45 -0
  24. qtype/examples/rag/recipe_chatbot.qtype.yaml +216 -0
  25. qtype/interpreter/auth/aws.py +94 -17
  26. qtype/interpreter/auth/generic.py +11 -12
  27. qtype/interpreter/base/secrets.py +4 -2
  28. qtype/interpreter/conversions.py +15 -14
  29. qtype/interpreter/converters.py +1 -1
  30. qtype/interpreter/executors/bedrock_reranker_executor.py +17 -28
  31. qtype/interpreter/executors/document_embedder_executor.py +1 -12
  32. qtype/interpreter/executors/invoke_embedding_executor.py +23 -33
  33. qtype/interpreter/executors/llm_inference_executor.py +2 -0
  34. qtype/interpreter/executors/sql_source_executor.py +6 -2
  35. qtype/interpreter/flow.py +11 -1
  36. qtype/mcp/server.py +11 -158
  37. qtype/semantic/visualize.py +10 -3
  38. {qtype-0.1.13.dist-info → qtype-0.1.14.dist-info}/METADATA +2 -2
  39. {qtype-0.1.13.dist-info → qtype-0.1.14.dist-info}/RECORD +42 -33
  40. {qtype-0.1.13.dist-info → qtype-0.1.14.dist-info}/WHEEL +0 -0
  41. {qtype-0.1.13.dist-info → qtype-0.1.14.dist-info}/entry_points.txt +0 -0
  42. {qtype-0.1.13.dist-info → qtype-0.1.14.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,8 @@
2
2
 
3
3
  Query relational databases and process results row-by-row using the `SQLSource` step, which supports any database accessible via SQLAlchemy connection strings.
4
4
 
5
+ **Note:** SQLSource is a source step that generates data from database queries, so flows using it typically require no inputs.
6
+
5
7
  ### QType YAML
6
8
 
7
9
  ```yaml
@@ -34,14 +34,15 @@ qtype run [options] spec
34
34
  #### Options
35
35
 
36
36
  - **`-f FLOW, --flow FLOW`** - The name of the flow to run. If not specified, runs the first flow found
37
- - **`-i INPUT, --input INPUT`** - JSON blob of input values for the flow (default: `{}`)
37
+ - **`-i INPUT, --input INPUT`** - JSON blob of input values for the flow (optional - omit for flows with source steps that generate their own data)
38
38
  - **`-I INPUT_FILE, --input-file INPUT_FILE`** - Path to a file (e.g., CSV, JSON, Parquet) with input data for batch processing
39
39
  - **`-o OUTPUT, --output OUTPUT`** - Path to save output data. If input is a DataFrame, output will be saved as parquet. If single result, saved as JSON
40
40
  - **`--progress`** - Show progress bars during flow execution
41
+ - **`--show-output`** - Display full output data in console (default: summary only)
41
42
 
42
43
  #### Examples
43
44
 
44
- Run a simple application:
45
+ Run a simple application with no inputs (e.g., flows with source steps):
45
46
  ```bash
46
47
  qtype run app.qtype.yaml
47
48
  ```
@@ -93,7 +93,3 @@ my-qtype-plugin/
93
93
  └── tests/
94
94
  └── test_plugin.py
95
95
  ```
96
-
97
- ## See Also
98
-
99
- - [CLI Reference](cli.md)
@@ -176,9 +176,4 @@ This document lists all semantic validation rules enforced by QType. These rules
176
176
  ## VectorSearch
177
177
 
178
178
  - Must have exactly 1 input of type `text`
179
- - Must have exactly 1 output of type `list[RAGSearchResult]`
180
-
181
- ## See Also
182
-
183
- - [Validate QType YAML](../How%20To/Observability%20%26%20Debugging/validate_qtype_yaml.md)
184
- - [CLI Reference](cli.md)
179
+ - Must have exactly 1 output of type `list[RAGSearchResult]`
@@ -180,7 +180,7 @@ Replace `sk-your-key-here` with your actual OpenAI API key.
180
180
  Run your application:
181
181
 
182
182
  ```bash
183
- qtype run -i '{"question":"What is 2+2?"}' 01_hello_world.qtype.yaml
183
+ qtype run -i '{"question":"What is 2+2?"}' --show-output 01_hello_world.qtype.yaml
184
184
  ```
185
185
 
186
186
  **What you should see:**
@@ -316,7 +316,7 @@ Should pass ✅
316
316
  ### Run It!
317
317
 
318
318
  ```bash
319
- qtype run -i '{"review_text":"These headphones are amazing! Great sound quality and super comfortable. Battery lasts all day."}' 03_structured_data.qtype.yaml
319
+ qtype run -i '{"review_text":"These headphones are amazing! Great sound quality and super comfortable. Battery lasts all day."}' --show-output 03_structured_data.qtype.yaml
320
320
  ```
321
321
 
322
322
  **Expected output:**
@@ -305,7 +305,7 @@ Should pass ✅
305
305
  ### Run the Application
306
306
 
307
307
  ```bash
308
- qtype run -i '{"days_until_due": 3}' 04_tools_and_function_calling.qtype.yaml
308
+ qtype run -i '{"days_until_due": 3}' --show-output 04_tools_and_function_calling.qtype.yaml
309
309
  ```
310
310
 
311
311
  **Expected output:**
@@ -0,0 +1,48 @@
1
+ id: simple_chatbot
2
+ description: A friendly chatbot with conversation memory using AWS Bedrock
3
+
4
+ # AWS Authentication for Bedrock
5
+ auths:
6
+ - type: aws
7
+ id: aws_auth
8
+ profile_name: ${AWS_PROFILE}
9
+
10
+ models:
11
+ - type: Model
12
+ id: nova_lite
13
+ provider: aws-bedrock
14
+ model_id: amazon.nova-lite-v1:0
15
+ inference_params:
16
+ temperature: 0.7
17
+ max_tokens: 512
18
+ auth: aws_auth
19
+ memories:
20
+ - id: conversation_memory
21
+ token_limit: 10000
22
+ flows:
23
+ - type: Flow
24
+ id: chat_flow
25
+ interface:
26
+ type: Conversational
27
+ variables:
28
+ - id: user_message
29
+ type: ChatMessage
30
+ - id: assistant_response
31
+ type: ChatMessage
32
+ inputs:
33
+ - user_message
34
+ outputs:
35
+ - assistant_response
36
+ steps:
37
+ - id: generate_response
38
+ type: LLMInference
39
+ model: nova_lite
40
+ system_message: |
41
+ You are a friendly and helpful chatbot. You have a warm, conversational
42
+ tone and enjoy helping users with their questions. You remember context
43
+ from previous messages in the conversation.
44
+ memory: conversation_memory
45
+ inputs:
46
+ - user_message
47
+ outputs:
48
+ - assistant_response
@@ -0,0 +1,31 @@
1
+ # Load all markdown files from docs directory using DocumentSource
2
+ #
3
+ # This example demonstrates using DocumentSource with SimpleDirectoryReader
4
+ # to load documents from a local directory with file filtering.
5
+
6
+ id: load_documents_example
7
+ description: Load markdown files from docs directory
8
+
9
+ flows:
10
+ - type: Flow
11
+ id: load_md_files
12
+ description: Load all markdown files from docs directory
13
+
14
+ variables:
15
+ - id: document
16
+ type: RAGDocument
17
+
18
+ inputs: []
19
+ outputs:
20
+ - document
21
+
22
+ steps:
23
+ - type: DocumentSource
24
+ id: md_docs
25
+ reader_module: llama_index.core.SimpleDirectoryReader
26
+ args:
27
+ input_dir: docs
28
+ required_exts: [".md"]
29
+ recursive: true
30
+ outputs:
31
+ - document
@@ -0,0 +1,45 @@
1
+ id: invoke_embedding_test
2
+ description: Test InvokeEmbedding step with AWS Bedrock embeddings
3
+
4
+ # AWS Authentication for Bedrock
5
+ auths:
6
+ - type: aws
7
+ id: aws_auth
8
+ profile_name: ${AWS_PROFILE}
9
+
10
+ models:
11
+ # Embedding model using AWS Bedrock
12
+ - type: EmbeddingModel
13
+ id: titan_embed
14
+ provider: aws-bedrock
15
+ model_id: amazon.titan-embed-text-v2:0
16
+ dimensions: 1024
17
+ auth: aws_auth
18
+
19
+ flows:
20
+ - type: Flow
21
+ id: test_invoke_embedding
22
+ description: Generate embeddings for text using AWS Bedrock
23
+
24
+ variables:
25
+ - id: input_text
26
+ type: text
27
+ ui:
28
+ widget: textarea
29
+ - id: embedding
30
+ type: Embedding
31
+
32
+ inputs:
33
+ - input_text
34
+
35
+ outputs:
36
+ - embedding
37
+
38
+ steps:
39
+ - id: generate_embedding
40
+ type: InvokeEmbedding
41
+ model: titan_embed
42
+ inputs:
43
+ - input_text
44
+ outputs:
45
+ - embedding
@@ -0,0 +1,216 @@
1
+ id: recipe_rag_chatbot
2
+ description: |
3
+ RAG chatbot for the Chowdown recipe collection from GitHub.
4
+
5
+ This application provides two flows:
6
+
7
+ 1. recipe_chat: Conversational chatbot that answers questions about recipes
8
+ - Uses RAG to find relevant recipes based on user questions
9
+ - Maintains conversation history with memory
10
+ - Provides cooking advice, recipe recommendations, and ingredient information
11
+
12
+ 2. recipe_ingestion: Ingests recipe markdown files into vector database
13
+ - Clones/fetches recipes from GitHub (clarklab/chowdown)
14
+ - Splits recipe documents into searchable chunks
15
+ - Generates embeddings using AWS Bedrock Titan
16
+ - Stores in Qdrant vector database for fast similarity search
17
+
18
+ Prerequisites:
19
+ - AWS credentials configured (AWS_PROFILE environment variable)
20
+ - Qdrant running locally on port 6333 (or update args for Qdrant Cloud)
21
+ - Clone the recipe repo: git clone https://github.com/clarklab/chowdown.git
22
+
23
+ To ingest recipes:
24
+ qtype run recipe_chatbot.qtype.yaml --flow recipe_ingestion
25
+
26
+ To start chatbot:
27
+ qtype serve recipe_chatbot.qtype.yaml --flow recipe_chat
28
+
29
+ # AWS Authentication for Bedrock
30
+ auths:
31
+ - type: aws
32
+ id: aws_auth
33
+ profile_name: ${AWS_PROFILE}
34
+
35
+ # Models
36
+ models:
37
+ # Embedding model for vectorizing recipes and queries
38
+ - type: EmbeddingModel
39
+ id: titan_embed
40
+ provider: aws-bedrock
41
+ model_id: amazon.titan-embed-text-v2:0
42
+ dimensions: 1024
43
+ auth: aws_auth
44
+
45
+ # Chat model for conversational responses
46
+ - type: Model
47
+ id: claude_sonnet
48
+ provider: aws-bedrock
49
+ model_id: us.anthropic.claude-3-5-sonnet-20241022-v2:0
50
+ inference_params:
51
+ temperature: 0.7
52
+ max_tokens: 4096
53
+ auth: aws_auth
54
+
55
+ # Vector index for recipe embeddings
56
+ indexes:
57
+ - type: VectorIndex
58
+ module: llama_index.vector_stores.qdrant.QdrantVectorStore
59
+ id: recipe_index
60
+ name: chowdown_recipes
61
+ embedding_model: titan_embed
62
+ args:
63
+ collection_name: chowdown_recipes
64
+ url: http://localhost:6333
65
+ api_key: "" # Empty for local Qdrant
66
+
67
+ # Memory for maintaining conversation context
68
+ memories:
69
+ - id: recipe_chat_memory
70
+ token_limit: 10000
71
+ chat_history_token_ratio: 0.7
72
+
73
+ # Flows
74
+ flows:
75
+ # Conversational chatbot flow
76
+ - type: Flow
77
+ id: recipe_chat
78
+ description: Chat with the recipe collection using RAG
79
+
80
+ interface:
81
+ type: Conversational
82
+
83
+ variables:
84
+ - id: user_message
85
+ type: ChatMessage
86
+ - id: user_question
87
+ type: text
88
+ - id: search_results
89
+ type: list[RAGSearchResult]
90
+ - id: context_prompt
91
+ type: text
92
+ - id: assistant_response
93
+ type: ChatMessage
94
+
95
+ inputs:
96
+ - user_message
97
+
98
+ outputs:
99
+ - assistant_response
100
+
101
+ steps:
102
+ # Extract text from user's chat message
103
+ - id: extract_question
104
+ type: FieldExtractor
105
+ json_path: "$.blocks[?(@.type == 'text')].content"
106
+ inputs:
107
+ - user_message
108
+ outputs:
109
+ - user_question
110
+
111
+ # Search recipe vector index for relevant recipes
112
+ - id: search_recipes
113
+ type: VectorSearch
114
+ index: recipe_index
115
+ default_top_k: 5
116
+ inputs:
117
+ - user_question
118
+ outputs:
119
+ - search_results
120
+
121
+ # Build prompt with recipe context
122
+ - id: build_context_prompt
123
+ type: PromptTemplate
124
+ template: |
125
+ You are a helpful cooking assistant with access to a collection of recipes from Chowdown.
126
+
127
+ Here are the most relevant recipes based on the user's question:
128
+
129
+ {search_results}
130
+
131
+ User question: {user_question}
132
+
133
+ Please provide a helpful answer based on the recipes above. If you're suggesting a recipe,
134
+ include key ingredients and brief cooking instructions. If the recipes don't contain
135
+ relevant information, politely say so and offer general cooking advice if appropriate.
136
+ inputs:
137
+ - search_results
138
+ - user_question
139
+ outputs:
140
+ - context_prompt
141
+
142
+ # Generate conversational response using LLM with memory
143
+ - id: generate_response
144
+ type: LLMInference
145
+ model: claude_sonnet
146
+ memory: recipe_chat_memory
147
+ system_message: |
148
+ You are a friendly and knowledgeable cooking assistant. You help users find recipes,
149
+ answer questions about ingredients, suggest substitutions, and provide cooking tips.
150
+ Base your answers on the provided recipe context, but feel free to add general
151
+ cooking knowledge when helpful. Be conversational and enthusiastic about food!
152
+ inputs:
153
+ - context_prompt
154
+ outputs:
155
+ - assistant_response
156
+
157
+ # Recipe ingestion flow
158
+ - type: Flow
159
+ id: recipe_ingestion
160
+ description: Load recipes from local GitHub clone, chunk, embed, and index
161
+
162
+ variables:
163
+ - id: recipe_document
164
+ type: RAGDocument
165
+ - id: recipe_chunk
166
+ type: RAGChunk
167
+ - id: embedded_chunk
168
+ type: RAGChunk
169
+
170
+ outputs:
171
+ - embedded_chunk
172
+
173
+ steps:
174
+ # Load recipe markdown files from local clone
175
+ - id: load_recipes
176
+ type: DocumentSource
177
+ reader_module: llama_index.core.SimpleDirectoryReader
178
+ args:
179
+ input_dir: "./chowdown/_recipes"
180
+ recursive: false
181
+ required_exts: [".md"]
182
+ outputs:
183
+ - recipe_document
184
+
185
+ # Split recipes into chunks for better retrieval
186
+ - id: split_recipes
187
+ type: DocumentSplitter
188
+ splitter_name: "SentenceSplitter"
189
+ chunk_size: 512
190
+ chunk_overlap: 50
191
+ inputs:
192
+ - recipe_document
193
+ outputs:
194
+ - recipe_chunk
195
+
196
+ # Generate embeddings for each chunk
197
+ - id: embed_chunks
198
+ type: DocumentEmbedder
199
+ model: titan_embed
200
+ concurrency_config:
201
+ num_workers: 5
202
+ inputs:
203
+ - recipe_chunk
204
+ outputs:
205
+ - embedded_chunk
206
+
207
+ # Store embedded chunks in Qdrant
208
+ - id: index_recipes
209
+ type: IndexUpsert
210
+ index: recipe_index
211
+ batch_config:
212
+ batch_size: 25
213
+ inputs:
214
+ - embedded_chunk
215
+ outputs:
216
+ - embedded_chunk
@@ -8,6 +8,7 @@ AWSAuthProvider configuration from the semantic model.
8
8
  from __future__ import annotations
9
9
 
10
10
  from contextlib import contextmanager
11
+ from dataclasses import asdict, dataclass
11
12
  from typing import Any, Generator
12
13
 
13
14
  import boto3 # type: ignore[import-untyped]
@@ -27,6 +28,30 @@ class AWSAuthenticationError(Exception):
27
28
  pass
28
29
 
29
30
 
31
+ @dataclass
32
+ class AWSCredentials:
33
+ """Resolved AWS credentials for creating boto3/aioboto3 clients.
34
+
35
+ This dataclass holds the resolved AWS credentials that can be passed
36
+ directly to both boto3 and aioboto3 constructors, ensuring async
37
+ operations work correctly without botocore session issues.
38
+ """
39
+
40
+ aws_access_key_id: str | None = None
41
+ aws_secret_access_key: str | None = None
42
+ aws_session_token: str | None = None
43
+ region_name: str | None = None
44
+ profile_name: str | None = None
45
+
46
+ def as_kwargs(self) -> dict[str, Any]:
47
+ """Return non-None credentials as kwargs dict.
48
+
49
+ Returns:
50
+ Dictionary of credential parameters with None values filtered out.
51
+ """
52
+ return {k: v for k, v in asdict(self).items() if v is not None}
53
+
54
+
30
55
  def _is_session_valid(session: boto3.Session) -> bool:
31
56
  """
32
57
  Check if a boto3 session is still valid by testing credential access.
@@ -60,14 +85,18 @@ def _is_session_valid(session: boto3.Session) -> bool:
60
85
  def aws(
61
86
  aws_provider: AWSAuthProvider,
62
87
  secret_manager: SecretManagerBase,
63
- ) -> Generator[boto3.Session, None, None]:
88
+ ) -> Generator[AWSCredentials, None, None]:
64
89
  """
65
- Create a boto3 Session using AWS authentication provider configuration.
90
+ Resolve AWS credentials from provider configuration.
91
+
92
+ This context manager resolves AWS credentials based on the authentication
93
+ method specified in the AWSAuthProvider and returns them as an
94
+ AWSCredentials dataclass. The credentials can be passed directly to both
95
+ boto3 and aioboto3 constructors, ensuring async operations work correctly.
66
96
 
67
- This context manager creates a boto3 Session based on the authentication
68
- method specified in the AWSAuthProvider. Sessions are cached using an LRU
69
- cache to avoid recreating them unnecessarily. The cache size can be configured
70
- via the AUTH_CACHE_MAX_SIZE environment variable (default: 128).
97
+ Sessions are cached using an LRU cache to avoid recreating them
98
+ unnecessarily. The cache size can be configured via the AUTH_CACHE_MAX_SIZE
99
+ environment variable (default: 128).
71
100
 
72
101
  It supports:
73
102
  - Direct credentials (access key + secret key + optional session token)
@@ -81,13 +110,17 @@ def aws(
81
110
  - Invalid or expired sessions are evicted and recreated
82
111
 
83
112
  Args:
84
- aws_provider: AWSAuthProvider instance containing authentication configuration
113
+ aws_provider: AWSAuthProvider instance containing authentication
114
+ configuration
115
+ secret_manager: SecretManagerBase for resolving SecretReferences
85
116
 
86
117
  Yields:
87
- boto3.Session: Configured boto3 session ready for creating AWS service clients
118
+ AWSCredentials: Resolved credentials ready for creating AWS service
119
+ clients
88
120
 
89
121
  Raises:
90
- AWSAuthenticationError: When authentication fails or configuration is invalid
122
+ AWSAuthenticationError: When authentication fails or configuration is
123
+ invalid
91
124
 
92
125
  Example:
93
126
  ```python
@@ -102,9 +135,13 @@ def aws(
102
135
  region="us-east-1"
103
136
  )
104
137
 
105
- with aws(aws_auth) as session:
106
- athena_client = session.client("athena")
107
- s3_client = session.client("s3")
138
+ with aws(aws_auth, secret_manager) as creds:
139
+ # Use with boto3
140
+ client = boto3.client("s3", **creds.as_kwargs())
141
+
142
+ # Or with llama-index Bedrock
143
+ from llama_index.llms.bedrock_converse import BedrockConverse
144
+ llm = BedrockConverse(**creds.as_kwargs(), model="...")
108
145
  ```
109
146
  """
110
147
  try:
@@ -112,8 +149,8 @@ def aws(
112
149
  cached_session = get_cached_auth(aws_provider)
113
150
 
114
151
  if cached_session is not None and _is_session_valid(cached_session):
115
- # Cache hit with valid session
116
- yield cached_session
152
+ # Cache hit with valid session - extract credentials from it
153
+ yield _extract_credentials(cached_session, aws_provider)
117
154
  return
118
155
 
119
156
  # Cache miss or invalid session - create new session
@@ -123,13 +160,15 @@ def aws(
123
160
  credentials = session.get_credentials()
124
161
  if credentials is None:
125
162
  raise AWSAuthenticationError(
126
- f"Failed to obtain AWS credentials for provider '{aws_provider.id}'"
163
+ f"Failed to obtain AWS credentials for provider "
164
+ f"'{aws_provider.id}'"
127
165
  )
128
166
 
129
167
  # Cache the valid session using provider object as key
130
168
  cache_auth(aws_provider, session)
131
169
 
132
- yield session
170
+ # Extract and yield credentials
171
+ yield _extract_credentials(session, aws_provider)
133
172
 
134
173
  except (ClientError, NoCredentialsError) as e:
135
174
  raise AWSAuthenticationError(
@@ -137,10 +176,48 @@ def aws(
137
176
  ) from e
138
177
  except Exception as e:
139
178
  raise AWSAuthenticationError(
140
- f"Unexpected error during AWS authentication for provider '{aws_provider.id}': {e}"
179
+ f"Unexpected error during AWS authentication for provider "
180
+ f"'{aws_provider.id}': {e}"
141
181
  ) from e
142
182
 
143
183
 
184
+ def _extract_credentials(
185
+ session: boto3.Session, aws_provider: AWSAuthProvider
186
+ ) -> AWSCredentials:
187
+ """
188
+ Extract credentials from a boto3 Session.
189
+
190
+ This function extracts the actual credential values from a boto3.Session,
191
+ including temporary credentials from role assumption. This allows the
192
+ credentials to be passed directly to boto3/aioboto3 constructors.
193
+
194
+ Args:
195
+ session: The boto3 session to extract credentials from
196
+ aws_provider: AWSAuthProvider for region/profile information
197
+
198
+ Returns:
199
+ AWSCredentials with the extracted credential values
200
+ """
201
+ credentials = session.get_credentials()
202
+
203
+ if credentials is None:
204
+ # No explicit credentials - use profile or environment
205
+ return AWSCredentials(
206
+ profile_name=aws_provider.profile_name,
207
+ region_name=session.region_name or aws_provider.region,
208
+ )
209
+
210
+ # Extract frozen credentials (works for both static and temporary)
211
+ frozen = credentials.get_frozen_credentials()
212
+
213
+ return AWSCredentials(
214
+ aws_access_key_id=frozen.access_key,
215
+ aws_secret_access_key=frozen.secret_key,
216
+ aws_session_token=frozen.token,
217
+ region_name=session.region_name or aws_provider.region,
218
+ )
219
+
220
+
144
221
  def _create_session(
145
222
  aws_provider: AWSAuthProvider,
146
223
  secret_manager: SecretManagerBase,
@@ -44,9 +44,7 @@ from __future__ import annotations
44
44
  from contextlib import contextmanager
45
45
  from typing import Generator
46
46
 
47
- import boto3 # type: ignore[import-untyped]
48
-
49
- from qtype.interpreter.auth.aws import aws
47
+ from qtype.interpreter.auth.aws import AWSCredentials, aws
50
48
  from qtype.interpreter.base.secrets import SecretManagerBase
51
49
  from qtype.semantic.model import (
52
50
  APIKeyAuthProvider,
@@ -111,13 +109,13 @@ def resolve_provider_secrets(
111
109
  def auth(
112
110
  auth_provider: AuthorizationProvider,
113
111
  secret_manager: SecretManagerBase,
114
- ) -> Generator[boto3.Session | AuthorizationProvider, None, None]:
112
+ ) -> Generator[AWSCredentials | AuthorizationProvider, None, None]:
115
113
  """
116
- Create an appropriate session or provider instance based on the auth provider type.
114
+ Create appropriate credentials or provider instance based on auth provider type.
117
115
 
118
116
  This context manager dispatches to the appropriate authentication handler based
119
117
  on the type of AuthorizationProvider:
120
- - AWSAuthProvider: Returns a configured boto3.Session
118
+ - AWSAuthProvider: Returns AWSCredentials with resolved credentials
121
119
  - APIKeyAuthProvider: Returns the provider instance (contains the API key)
122
120
 
123
121
  Args:
@@ -125,7 +123,7 @@ def auth(
125
123
  secret_manager: Secret manager for resolving SecretReferences
126
124
 
127
125
  Yields:
128
- boto3.Session | APIKeyAuthProvider: The appropriate session or provider instance
126
+ AWSCredentials | AuthorizationProvider: The appropriate credentials or provider instance
129
127
 
130
128
  Raises:
131
129
  UnsupportedAuthProviderError: When an unsupported provider type is used
@@ -135,7 +133,7 @@ def auth(
135
133
  from qtype.semantic.model import AWSAuthProvider, APIKeyAuthProvider
136
134
  from qtype.interpreter.auth.generic import auth
137
135
 
138
- # AWS provider - returns boto3.Session
136
+ # AWS provider - returns AWSCredentials
139
137
  aws_auth = AWSAuthProvider(
140
138
  id="my-aws-auth",
141
139
  type="aws",
@@ -144,8 +142,9 @@ def auth(
144
142
  region="us-east-1"
145
143
  )
146
144
 
147
- with auth(aws_auth) as session:
148
- s3_client = session.client("s3")
145
+ with auth(aws_auth, secret_manager) as creds:
146
+ import boto3
147
+ s3_client = boto3.client("s3", **creds.as_kwargs())
149
148
 
150
149
  # API Key provider - returns the provider itself
151
150
  api_auth = APIKeyAuthProvider(
@@ -161,8 +160,8 @@ def auth(
161
160
  """
162
161
  if isinstance(auth_provider, AWSAuthProvider):
163
162
  # Use AWS-specific context manager
164
- with aws(auth_provider, secret_manager) as session:
165
- yield session
163
+ with aws(auth_provider, secret_manager) as creds:
164
+ yield creds
166
165
 
167
166
  elif isinstance(auth_provider, (APIKeyAuthProvider, OAuth2AuthProvider)):
168
167
  # For non-AWS providers, resolve secrets and yield modified copy
@@ -255,10 +255,12 @@ class AWSSecretManager(SecretManagerBase):
255
255
  json.JSONDecodeError: If secret is not valid JSON when key
256
256
  is specified
257
257
  """
258
+ import boto3
259
+
258
260
  from qtype.interpreter.auth.aws import aws
259
261
 
260
- with aws(self.config.auth) as session: # type: ignore
261
- client = session.client("secretsmanager")
262
+ with aws(self.config.auth) as creds: # type: ignore
263
+ client = boto3.client("secretsmanager", **creds.as_kwargs())
262
264
  response = client.get_secret_value(SecretId=secret_ref.secret_name)
263
265
 
264
266
  if "SecretString" not in response: