snowflake-cortex-agent-sdk 0.0.1__tar.gz → 0.0.2__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.
Files changed (76) hide show
  1. snowflake_cortex_agent_sdk-0.0.2/PKG-INFO +118 -0
  2. snowflake_cortex_agent_sdk-0.0.2/README.md +90 -0
  3. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/__init__.py +126 -0
  4. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_base_client.py +39 -8
  5. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_generated/agent_runs.py +198 -5
  6. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/_generated/data_agent.py +4733 -0
  7. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_generated/events.py +640 -45
  8. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/_generated/data_agent.py → snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/_generated/lite_agent.py +198 -289
  9. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_generated/threads.py +52 -2
  10. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_version.py +1 -1
  11. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/api.py +83 -0
  12. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/client.py +9 -6
  13. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/conversation.py +295 -0
  14. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/__init__.py +1 -36
  15. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/client.py +1 -15
  16. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/fork_session.py +9 -22
  17. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/types.py +5 -55
  18. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/errors.py +33 -0
  19. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/public_client.py +56 -0
  20. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/coding_agent.py +83 -15
  21. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/runs.py +84 -0
  22. snowflake_cortex_agent_sdk-0.0.2/cortex_agent_sdk/turn.py +85 -0
  23. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/views/agent_response.py +52 -4
  24. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/views/async_run.py +76 -0
  25. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/pyproject.toml +5 -7
  26. snowflake_cortex_agent_sdk-0.0.1/PKG-INFO +0 -194
  27. snowflake_cortex_agent_sdk-0.0.1/README.md +0 -165
  28. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/__init__.py +0 -180
  29. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/_generated/lite_agent.py +0 -1050
  30. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/conversation.py +0 -428
  31. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api.py +0 -988
  32. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/__init__.py +0 -1
  33. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/connection.py +0 -232
  34. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/messages.py +0 -72
  35. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/options.py +0 -100
  36. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/settings.py +0 -31
  37. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/tools.py +0 -55
  38. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/trace.py +0 -99
  39. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/types.py +0 -86
  40. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/utils.py +0 -34
  41. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/_internal/transport/api_support/wire.py +0 -530
  42. snowflake_cortex_agent_sdk-0.0.1/cortex_agent_sdk/cortexcode/threads.py +0 -190
  43. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/.gitignore +0 -0
  44. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/LICENSE +0 -0
  45. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_constants.py +0 -0
  46. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_generated/__init__.py +0 -0
  47. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_request_options.py +0 -0
  48. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_resource.py +0 -0
  49. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_transport.py +0 -0
  50. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/_types.py +0 -0
  51. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/api_response.py +0 -0
  52. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/auth.py +0 -0
  53. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/constants.py +0 -0
  54. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_errors.py +0 -0
  55. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_internal/__init__.py +0 -0
  56. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_internal/message_parser.py +0 -0
  57. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_internal/query.py +0 -0
  58. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_internal/transport/__init__.py +0 -0
  59. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/_internal/transport/subprocess_cli.py +0 -0
  60. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/py.typed +0 -0
  61. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/cortexcode/query.py +0 -0
  62. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/models.py +0 -0
  63. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/origin.py +0 -0
  64. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/py.typed +0 -0
  65. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/__init__.py +0 -0
  66. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/agent.py +0 -0
  67. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/feedback.py +0 -0
  68. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/resources/threads.py +0 -0
  69. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/stream.py +0 -0
  70. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/streaming/__init__.py +0 -0
  71. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/streaming/parser.py +0 -0
  72. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/streaming/run_stream.py +0 -0
  73. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/threads_page.py +0 -0
  74. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/views/__init__.py +0 -0
  75. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/views/content_view.py +0 -0
  76. {snowflake_cortex_agent_sdk-0.0.1 → snowflake_cortex_agent_sdk-0.0.2}/cortex_agent_sdk/views/thread_message.py +0 -0
@@ -0,0 +1,118 @@
1
+ Metadata-Version: 2.5
2
+ Name: snowflake-cortex-agent-sdk
3
+ Version: 0.0.2
4
+ Summary: Python SDK for the Snowflake Cortex Agent REST API (agent:run streaming, threads, feedback)
5
+ Author-email: "Snowflake, Inc." <support@snowflake.com>
6
+ License: See LICENSE
7
+ License-File: LICENSE
8
+ Keywords: cortex,cortex-agent,openapi,sdk,snowflake
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3 :: Only
11
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: anyio>=4.0
14
+ Requires-Dist: httpx-sse>=0.4
15
+ Requires-Dist: httpx>=0.27
16
+ Requires-Dist: mcp<2,>=1.0
17
+ Requires-Dist: pydantic>=2.0
18
+ Requires-Dist: snowflake-connector-python[secure-local-storage]<5,>=4.7.1
19
+ Requires-Dist: typing-extensions>=4.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: datamodel-code-generator>=0.28; extra == 'dev'
22
+ Requires-Dist: mypy>=1.0; extra == 'dev'
23
+ Requires-Dist: pytest-asyncio>=0.20.0; extra == 'dev'
24
+ Requires-Dist: pytest-timeout>=2.0.0; extra == 'dev'
25
+ Requires-Dist: pytest>=8.0; extra == 'dev'
26
+ Requires-Dist: ruff>=0.6; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Snowflake Cortex Agent SDK for Python
30
+
31
+ The SDK centers on conversations, durable turns, and completed responses. It
32
+ uses background runs internally while hiding run and thread bookkeeping.
33
+
34
+ ```python
35
+ from cortex_agent_sdk import (
36
+ AgentResponse,
37
+ Conversation,
38
+ ConversationMessage,
39
+ CortexAgentClient,
40
+ Turn,
41
+ )
42
+
43
+ client: CortexAgentClient = CortexAgentClient(account="my_account", auth="<token>")
44
+ conversation: Conversation = client.conversations.create(
45
+ origin_application="cortex_agent_sdk",
46
+ agent={"type": "inline", "spec": {"models": {"orchestration": "auto"}}},
47
+ )
48
+ turn: Turn = conversation.send("What is Snowflake Arctic?")
49
+ answer: AgentResponse = turn.result()
50
+ print(answer.text)
51
+
52
+ history: list[ConversationMessage] = conversation.history()
53
+ ```
54
+
55
+ Set a default agent when creating a conversation. A send can override it for
56
+ one turn when needed:
57
+
58
+ ```python
59
+ inline_turn: Turn = conversation.send(
60
+ "Summarize the context.",
61
+ agent={"type": "inline", "spec": {"models": {"orchestration": "auto"}}},
62
+ )
63
+
64
+ data_turn: Turn = conversation.send(
65
+ "Which products grew fastest?",
66
+ agent={
67
+ "type": "data",
68
+ "spec": {"database": "ANALYTICS", "schema": "PUBLIC", "name": "SALES_AGENT"},
69
+ },
70
+ )
71
+
72
+ coding_turn: Turn = conversation.send(
73
+ "Fix the failing test.",
74
+ agent={
75
+ "type": "coding",
76
+ "spec": {
77
+ "models": {"orchestration": "claude-sonnet-4-5"},
78
+ "permission_policy": "always_allow",
79
+ "workspace_mounts": [{"name": "USER$MY_USER.PUBLIC.DEFAULT$"}],
80
+ },
81
+ },
82
+ )
83
+ ```
84
+
85
+ Use `turn.events()` for event-level progress and `turn.cancel()` to stop a run.
86
+
87
+ The asynchronous API mirrors the sync API:
88
+
89
+ ```python
90
+ from cortex_agent_sdk import AgentResponse, AsyncConversation, AsyncCortexAgentClient, AsyncTurn
91
+
92
+ async with AsyncCortexAgentClient(account="my_account", auth="<token>") as client:
93
+ conversation: AsyncConversation = await client.conversations.create(
94
+ origin_application="cortex_agent_sdk", agent={"type": "inline", "spec": {}}
95
+ )
96
+ turn: AsyncTurn = await conversation.send("Analyze this data.")
97
+ answer: AgentResponse = await turn.result()
98
+ ```
99
+
100
+ The root client exposes `conversations` and `feedback`. Advanced streaming and
101
+ explicit run/thread management live under
102
+ `cortex_agent_sdk.api`:
103
+
104
+ ```python
105
+ from cortex_agent_sdk.api import CortexAgentClient
106
+
107
+ api_client: CortexAgentClient = CortexAgentClient(
108
+ account="my_account",
109
+ auth="<token>",
110
+ )
111
+ ```
112
+
113
+ Authentication headers are rebuilt for every attempt. `refresh_auth` runs only
114
+ for recognized token/session expiry responses; by default the SDK refreshes once
115
+ and replays the request once. SDK-generated errors do not include credential
116
+ values.
117
+
118
+ Requires Python 3.10 or newer.
@@ -0,0 +1,90 @@
1
+ # Snowflake Cortex Agent SDK for Python
2
+
3
+ The SDK centers on conversations, durable turns, and completed responses. It
4
+ uses background runs internally while hiding run and thread bookkeeping.
5
+
6
+ ```python
7
+ from cortex_agent_sdk import (
8
+ AgentResponse,
9
+ Conversation,
10
+ ConversationMessage,
11
+ CortexAgentClient,
12
+ Turn,
13
+ )
14
+
15
+ client: CortexAgentClient = CortexAgentClient(account="my_account", auth="<token>")
16
+ conversation: Conversation = client.conversations.create(
17
+ origin_application="cortex_agent_sdk",
18
+ agent={"type": "inline", "spec": {"models": {"orchestration": "auto"}}},
19
+ )
20
+ turn: Turn = conversation.send("What is Snowflake Arctic?")
21
+ answer: AgentResponse = turn.result()
22
+ print(answer.text)
23
+
24
+ history: list[ConversationMessage] = conversation.history()
25
+ ```
26
+
27
+ Set a default agent when creating a conversation. A send can override it for
28
+ one turn when needed:
29
+
30
+ ```python
31
+ inline_turn: Turn = conversation.send(
32
+ "Summarize the context.",
33
+ agent={"type": "inline", "spec": {"models": {"orchestration": "auto"}}},
34
+ )
35
+
36
+ data_turn: Turn = conversation.send(
37
+ "Which products grew fastest?",
38
+ agent={
39
+ "type": "data",
40
+ "spec": {"database": "ANALYTICS", "schema": "PUBLIC", "name": "SALES_AGENT"},
41
+ },
42
+ )
43
+
44
+ coding_turn: Turn = conversation.send(
45
+ "Fix the failing test.",
46
+ agent={
47
+ "type": "coding",
48
+ "spec": {
49
+ "models": {"orchestration": "claude-sonnet-4-5"},
50
+ "permission_policy": "always_allow",
51
+ "workspace_mounts": [{"name": "USER$MY_USER.PUBLIC.DEFAULT$"}],
52
+ },
53
+ },
54
+ )
55
+ ```
56
+
57
+ Use `turn.events()` for event-level progress and `turn.cancel()` to stop a run.
58
+
59
+ The asynchronous API mirrors the sync API:
60
+
61
+ ```python
62
+ from cortex_agent_sdk import AgentResponse, AsyncConversation, AsyncCortexAgentClient, AsyncTurn
63
+
64
+ async with AsyncCortexAgentClient(account="my_account", auth="<token>") as client:
65
+ conversation: AsyncConversation = await client.conversations.create(
66
+ origin_application="cortex_agent_sdk", agent={"type": "inline", "spec": {}}
67
+ )
68
+ turn: AsyncTurn = await conversation.send("Analyze this data.")
69
+ answer: AgentResponse = await turn.result()
70
+ ```
71
+
72
+ The root client exposes `conversations` and `feedback`. Advanced streaming and
73
+ explicit run/thread management live under
74
+ `cortex_agent_sdk.api`:
75
+
76
+ ```python
77
+ from cortex_agent_sdk.api import CortexAgentClient
78
+
79
+ api_client: CortexAgentClient = CortexAgentClient(
80
+ account="my_account",
81
+ auth="<token>",
82
+ )
83
+ ```
84
+
85
+ Authentication headers are rebuilt for every attempt. `refresh_auth` runs only
86
+ for recognized token/session expiry responses; by default the SDK refreshes once
87
+ and replays the request once. SDK-generated errors do not include credential
88
+ values.
89
+
90
+ Requires Python 3.10 or newer.
@@ -0,0 +1,126 @@
1
+ """Snowflake Cortex Agent SDK (Python).
2
+
3
+ Sync::
4
+
5
+ from cortex_agent_sdk import AgentResponse, Conversation, CortexAgentClient, Turn
6
+
7
+ client = CortexAgentClient(account="myacct", auth="<pat>")
8
+ conversation: Conversation = client.conversations.create(
9
+ origin_application="cortex_agent_sdk", agent={"type": "inline", "spec": {}}
10
+ )
11
+ turn: Turn = conversation.send("What is Snowflake Arctic?")
12
+ answer: AgentResponse = turn.result()
13
+ print(answer.text)
14
+
15
+ Async::
16
+
17
+ import asyncio
18
+ from cortex_agent_sdk import AsyncConversation, AsyncCortexAgentClient
19
+
20
+ async def main():
21
+ async with AsyncCortexAgentClient(account="myacct", auth="<pat>") as client:
22
+ conversation: AsyncConversation = await client.conversations.create(
23
+ origin_application="cortex_agent_sdk", agent={"type": "inline", "spec": {}}
24
+ )
25
+ turn: AsyncTurn = await conversation.send("What is Snowflake Arctic?")
26
+ answer: AgentResponse = await turn.result()
27
+ print(answer.text)
28
+
29
+ asyncio.run(main())
30
+
31
+ Start with ``docs/01-getting-started.md``.
32
+ """
33
+
34
+ from __future__ import annotations
35
+
36
+ from ._request_options import RequestOptions
37
+ from ._transport import AuthRefreshContext, RetryPolicy
38
+ from ._version import __title__, __version__
39
+ from .auth import (
40
+ Authenticator,
41
+ CallableAuth,
42
+ KeyPairJwtAuth,
43
+ OAuthAuth,
44
+ PatAuth,
45
+ SnowflakeSessionTokenAuth,
46
+ SnowflakeTokenAuth,
47
+ SnowflakeTokenType,
48
+ )
49
+ from .conversation import (
50
+ AgentSpec,
51
+ AsyncConversation,
52
+ CodingAgentSpec,
53
+ Conversation,
54
+ ConversationInput,
55
+ ConversationMessage,
56
+ DataAgentSpec,
57
+ InlineAgentSpec,
58
+ )
59
+ from .errors import (
60
+ APIConnectionAbortedError,
61
+ APIConnectionError,
62
+ APIStatusError,
63
+ APITimeoutError,
64
+ AuthenticationError,
65
+ AuthRefreshFailureContext,
66
+ BadRequestError,
67
+ ConflictError,
68
+ CortexAgentAPIError,
69
+ CortexAgentError,
70
+ InternalServerError,
71
+ NotFoundError,
72
+ PermissionDeniedError,
73
+ RateLimitError,
74
+ UnprocessableEntityError,
75
+ )
76
+ from .public_client import AsyncCortexAgentClient, CortexAgentClient
77
+ from .turn import AsyncTurn, Turn
78
+ from .views.agent_response import AgentResponseView as AgentResponse
79
+
80
+ __all__ = [
81
+ # Core clients + config
82
+ "AgentSpec",
83
+ "AgentResponse",
84
+ "AuthRefreshContext",
85
+ "AsyncConversation",
86
+ "AsyncCortexAgentClient",
87
+ "AsyncTurn",
88
+ "Conversation",
89
+ "ConversationInput",
90
+ "ConversationMessage",
91
+ "CodingAgentSpec",
92
+ "DataAgentSpec",
93
+ "InlineAgentSpec",
94
+ "CortexAgentClient",
95
+ "RequestOptions",
96
+ "RetryPolicy",
97
+ "Turn",
98
+ # Auth
99
+ "Authenticator",
100
+ "CallableAuth",
101
+ "KeyPairJwtAuth",
102
+ "OAuthAuth",
103
+ "PatAuth",
104
+ "SnowflakeSessionTokenAuth",
105
+ "SnowflakeTokenAuth",
106
+ "SnowflakeTokenType",
107
+ # Errors
108
+ "APIConnectionAbortedError",
109
+ "APIConnectionError",
110
+ "APIStatusError",
111
+ "APITimeoutError",
112
+ "AuthRefreshFailureContext",
113
+ "AuthenticationError",
114
+ "BadRequestError",
115
+ "ConflictError",
116
+ "CortexAgentAPIError",
117
+ "CortexAgentError",
118
+ "InternalServerError",
119
+ "NotFoundError",
120
+ "PermissionDeniedError",
121
+ "RateLimitError",
122
+ "UnprocessableEntityError",
123
+ # Metadata
124
+ "__title__",
125
+ "__version__",
126
+ ]
@@ -55,7 +55,9 @@ from .errors import (
55
55
  APIConnectionAbortedError,
56
56
  APIConnectionError,
57
57
  APITimeoutError,
58
+ AuthRefreshFailureContext,
58
59
  CortexAgentError,
60
+ attach_auth_refresh_failure_context,
59
61
  create_api_error,
60
62
  )
61
63
 
@@ -84,8 +86,11 @@ class BaseClient:
84
86
  ) -> None:
85
87
  if base_url is None and account is None:
86
88
  raise CortexAgentError("Either `account` or `base_url` is required.")
89
+ # Account identifiers may contain underscores, but DNS host labels use
90
+ # Snowflake's canonical hyphenated form.
91
+ account_host = account.replace("_", "-") if account is not None else None
87
92
  self.base_url = _validate_base_url(
88
- (base_url or f"https://{account}.snowflakecomputing.com").rstrip("/")
93
+ (base_url or f"https://{account_host}.snowflakecomputing.com").rstrip("/")
89
94
  )
90
95
  self.auth: Authenticator = coerce_authenticator(auth)
91
96
  self.retry_policy = merge_retry_policies(None, retry_policy)
@@ -540,10 +545,18 @@ class SyncAPIClient(BaseClient):
540
545
  try:
541
546
  self.refresh_auth(refresh_context)
542
547
  except Exception as exc:
543
- raise create_api_error(
548
+ api_error = create_api_error(
544
549
  response.status_code,
545
550
  refresh_context.request_id,
546
551
  refresh_context.response_body,
552
+ )
553
+ raise attach_auth_refresh_failure_context(
554
+ api_error,
555
+ AuthRefreshFailureContext(
556
+ attempt=refresh_context.attempt,
557
+ provider=_auth_provider_name(self.auth),
558
+ request_id=refresh_context.request_id,
559
+ ),
547
560
  ) from exc
548
561
  auth_refresh_attempt += 1
549
562
  continue
@@ -693,10 +706,18 @@ class AsyncAPIClient(BaseClient):
693
706
  try:
694
707
  await self._call_refresh_auth(refresh_context)
695
708
  except Exception as exc:
696
- raise create_api_error(
709
+ api_error = create_api_error(
697
710
  response.status_code,
698
711
  refresh_context.request_id,
699
712
  refresh_context.response_body,
713
+ )
714
+ raise attach_auth_refresh_failure_context(
715
+ api_error,
716
+ AuthRefreshFailureContext(
717
+ attempt=refresh_context.attempt,
718
+ provider=_auth_provider_name(self.auth),
719
+ request_id=refresh_context.request_id,
720
+ ),
700
721
  ) from exc
701
722
  auth_refresh_attempt += 1
702
723
  continue
@@ -711,11 +732,16 @@ class AsyncAPIClient(BaseClient):
711
732
  await asyncio.sleep(delay)
712
733
 
713
734
 
714
- #: Hosts where plaintext HTTP is acceptable, because the credential never leaves
715
- #: the machine. Anything else must be TLS.
735
+ #: Hosts where plaintext HTTP is acceptable in local development. Local GS uses
736
+ #: account-qualified ``*.local`` names (for example ``snowflake.reg.local``),
737
+ #: while public endpoints must remain TLS-only.
716
738
  _LOCAL_HOSTS = frozenset({"localhost", "127.0.0.1", "::1", "[::1]"})
717
739
 
718
740
 
741
+ def _is_local_host(hostname: str | None) -> bool:
742
+ return hostname in _LOCAL_HOSTS or (hostname is not None and hostname.endswith(".local"))
743
+
744
+
719
745
  def _validate_base_url(base_url: str) -> str:
720
746
  """Reject a base URL that would put the credential on the wire in clear text.
721
747
 
@@ -728,7 +754,7 @@ def _validate_base_url(base_url: str) -> str:
728
754
  parsed = urlparse(base_url)
729
755
  if parsed.scheme == "https":
730
756
  return base_url
731
- if parsed.scheme == "http" and parsed.hostname in _LOCAL_HOSTS:
757
+ if parsed.scheme == "http" and _is_local_host(parsed.hostname):
732
758
  return base_url
733
759
  if not parsed.scheme:
734
760
  raise CortexAgentError(
@@ -738,11 +764,16 @@ def _validate_base_url(base_url: str) -> str:
738
764
  f"base_url must use https — got {parsed.scheme!r} in {base_url!r}. Every "
739
765
  f"request sends a credential in an Authorization header, so a plaintext "
740
766
  f"endpoint would expose it to anyone on the network path. "
741
- f"(http:// is allowed for loopback hosts only: "
742
- f"{', '.join(sorted(_LOCAL_HOSTS))}.)"
767
+ "(http:// is allowed for local hosts only: localhost, loopback addresses, "
768
+ "or a *.local development endpoint.)"
743
769
  )
744
770
 
745
771
 
772
+ def _auth_provider_name(auth: Authenticator) -> str:
773
+ name = type(auth).__name__
774
+ return name if name != "object" else "Authenticator"
775
+
776
+
746
777
  def _parse_retry_after(headers: httpx.Headers) -> float | None:
747
778
  """Seconds from ``retry-after-ms`` (preferred), ``retry-after``, or an HTTP date."""
748
779
  ms = headers.get("retry-after-ms")