morphsdk 0.2.5__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 (82) hide show
  1. morphsdk-0.2.5/.gitignore +9 -0
  2. morphsdk-0.2.5/PKG-INFO +226 -0
  3. morphsdk-0.2.5/README.md +176 -0
  4. morphsdk-0.2.5/morphsdk/__init__.py +54 -0
  5. morphsdk-0.2.5/morphsdk/_agent/__init__.py +64 -0
  6. morphsdk-0.2.5/morphsdk/_agent/config.py +52 -0
  7. morphsdk-0.2.5/morphsdk/_agent/explore.py +276 -0
  8. morphsdk-0.2.5/morphsdk/_agent/github.py +57 -0
  9. morphsdk-0.2.5/morphsdk/_agent/helpers.py +133 -0
  10. morphsdk-0.2.5/morphsdk/_agent/parser.py +163 -0
  11. morphsdk-0.2.5/morphsdk/_agent/runner.py +524 -0
  12. morphsdk-0.2.5/morphsdk/_agent/tools.py +171 -0
  13. morphsdk-0.2.5/morphsdk/_agent/types.py +126 -0
  14. morphsdk-0.2.5/morphsdk/_base.py +309 -0
  15. morphsdk-0.2.5/morphsdk/_client.py +245 -0
  16. morphsdk-0.2.5/morphsdk/_config.py +37 -0
  17. morphsdk-0.2.5/morphsdk/_constants.py +53 -0
  18. morphsdk-0.2.5/morphsdk/_errors.py +111 -0
  19. morphsdk-0.2.5/morphsdk/_providers/__init__.py +36 -0
  20. morphsdk-0.2.5/morphsdk/_providers/_filter.py +92 -0
  21. morphsdk-0.2.5/morphsdk/_providers/base.py +94 -0
  22. morphsdk-0.2.5/morphsdk/_providers/code_storage_http.py +104 -0
  23. morphsdk-0.2.5/morphsdk/_providers/local.py +270 -0
  24. morphsdk-0.2.5/morphsdk/_providers/remote.py +161 -0
  25. morphsdk-0.2.5/morphsdk/_version.py +1 -0
  26. morphsdk-0.2.5/morphsdk/adapters/__init__.py +1 -0
  27. morphsdk-0.2.5/morphsdk/adapters/anthropic.py +360 -0
  28. morphsdk-0.2.5/morphsdk/adapters/langchain.py +120 -0
  29. morphsdk-0.2.5/morphsdk/adapters/openai.py +500 -0
  30. morphsdk-0.2.5/morphsdk/py.typed +0 -0
  31. morphsdk-0.2.5/morphsdk/resources/__init__.py +0 -0
  32. morphsdk-0.2.5/morphsdk/resources/browser.py +919 -0
  33. morphsdk-0.2.5/morphsdk/resources/compact.py +133 -0
  34. morphsdk-0.2.5/morphsdk/resources/edit.py +506 -0
  35. morphsdk-0.2.5/morphsdk/resources/explore.py +333 -0
  36. morphsdk-0.2.5/morphsdk/resources/git.py +861 -0
  37. morphsdk-0.2.5/morphsdk/resources/github.py +1214 -0
  38. morphsdk-0.2.5/morphsdk/resources/grep.py +583 -0
  39. morphsdk-0.2.5/morphsdk/resources/mobile.py +134 -0
  40. morphsdk-0.2.5/morphsdk/resources/reflex.py +414 -0
  41. morphsdk-0.2.5/morphsdk/resources/router.py +124 -0
  42. morphsdk-0.2.5/morphsdk/resources/search.py +110 -0
  43. morphsdk-0.2.5/morphsdk/tracing/__init__.py +70 -0
  44. morphsdk-0.2.5/morphsdk/tracing/_otel.py +101 -0
  45. morphsdk-0.2.5/morphsdk/tracing/core.py +249 -0
  46. morphsdk-0.2.5/morphsdk/tracing/interaction.py +284 -0
  47. morphsdk-0.2.5/morphsdk/tracing/otel.py +75 -0
  48. morphsdk-0.2.5/morphsdk/tracing/reflex.py +58 -0
  49. morphsdk-0.2.5/morphsdk/tracing/types.py +163 -0
  50. morphsdk-0.2.5/morphsdk/types/__init__.py +140 -0
  51. morphsdk-0.2.5/morphsdk/types/browser.py +118 -0
  52. morphsdk-0.2.5/morphsdk/types/compact.py +41 -0
  53. morphsdk-0.2.5/morphsdk/types/edit.py +31 -0
  54. morphsdk-0.2.5/morphsdk/types/explore.py +42 -0
  55. morphsdk-0.2.5/morphsdk/types/git.py +25 -0
  56. morphsdk-0.2.5/morphsdk/types/github.py +111 -0
  57. morphsdk-0.2.5/morphsdk/types/grep.py +41 -0
  58. morphsdk-0.2.5/morphsdk/types/mobile.py +25 -0
  59. morphsdk-0.2.5/morphsdk/types/reflex.py +137 -0
  60. morphsdk-0.2.5/morphsdk/types/router.py +21 -0
  61. morphsdk-0.2.5/morphsdk/types/search.py +33 -0
  62. morphsdk-0.2.5/pyproject.toml +81 -0
  63. morphsdk-0.2.5/tests/__init__.py +0 -0
  64. morphsdk-0.2.5/tests/integration/__init__.py +0 -0
  65. morphsdk-0.2.5/tests/integration/test_async_integration.py +40 -0
  66. morphsdk-0.2.5/tests/integration/test_explore_integration.py +72 -0
  67. morphsdk-0.2.5/tests/integration/test_grep_agent_integration.py +90 -0
  68. morphsdk-0.2.5/tests/integration/test_integration.py +102 -0
  69. morphsdk-0.2.5/tests/integration/test_reflex_integration.py +50 -0
  70. morphsdk-0.2.5/tests/test_adapters.py +68 -0
  71. morphsdk-0.2.5/tests/test_async_client.py +610 -0
  72. morphsdk-0.2.5/tests/test_browser.py +185 -0
  73. morphsdk-0.2.5/tests/test_client.py +86 -0
  74. morphsdk-0.2.5/tests/test_compact_params.py +86 -0
  75. morphsdk-0.2.5/tests/test_debug_logging.py +83 -0
  76. morphsdk-0.2.5/tests/test_errors.py +84 -0
  77. morphsdk-0.2.5/tests/test_explore.py +323 -0
  78. morphsdk-0.2.5/tests/test_grep_agent.py +455 -0
  79. morphsdk-0.2.5/tests/test_providers.py +359 -0
  80. morphsdk-0.2.5/tests/test_reflex.py +432 -0
  81. morphsdk-0.2.5/tests/test_tracing.py +475 -0
  82. morphsdk-0.2.5/tests/test_types.py +166 -0
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .venv/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ *.egg
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: morphsdk
3
+ Version: 0.2.5
4
+ Summary: Morph SDK - AI-powered code editing, search, browser automation, and more
5
+ Project-URL: Homepage, https://morphllm.com
6
+ Project-URL: Documentation, https://docs.morphllm.com
7
+ Author-email: Morph <support@morphllm.com>
8
+ License-Expression: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: eval-type-backport>=0.2; python_version < '3.10'
21
+ Requires-Dist: gitpython>=3.1
22
+ Requires-Dist: httpx>=0.25
23
+ Requires-Dist: pydantic>=2.0
24
+ Provides-Extra: all
25
+ Requires-Dist: anthropic>=0.30; extra == 'all'
26
+ Requires-Dist: langchain-core>=0.2; extra == 'all'
27
+ Requires-Dist: openai>=1.0; extra == 'all'
28
+ Requires-Dist: opentelemetry-api>=1.41; extra == 'all'
29
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.41; extra == 'all'
30
+ Requires-Dist: opentelemetry-sdk>=1.41; extra == 'all'
31
+ Requires-Dist: traceloop-sdk>=0.36; extra == 'all'
32
+ Provides-Extra: anthropic
33
+ Requires-Dist: anthropic>=0.30; extra == 'anthropic'
34
+ Provides-Extra: dev
35
+ Requires-Dist: mypy>=1.8; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
37
+ Requires-Dist: pytest>=8; extra == 'dev'
38
+ Requires-Dist: respx>=0.21; extra == 'dev'
39
+ Requires-Dist: ruff>=0.3; extra == 'dev'
40
+ Provides-Extra: langchain
41
+ Requires-Dist: langchain-core>=0.2; extra == 'langchain'
42
+ Provides-Extra: openai
43
+ Requires-Dist: openai>=1.0; extra == 'openai'
44
+ Provides-Extra: otel
45
+ Requires-Dist: opentelemetry-api>=1.41; extra == 'otel'
46
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.41; extra == 'otel'
47
+ Requires-Dist: opentelemetry-sdk>=1.41; extra == 'otel'
48
+ Requires-Dist: traceloop-sdk>=0.36; extra == 'otel'
49
+ Description-Content-Type: text/markdown
50
+
51
+ # Morph Python SDK
52
+
53
+ One typed client for Morph's specialized agent models: merge code edits with Fast Apply at ~10,500 tok/s, search a repo with WarpGrep, compress context with the compactor, route between models, and run browser tasks. Synchronous and async, Python 3.9+.
54
+
55
+ ```bash
56
+ pip install morphsdk
57
+ ```
58
+
59
+ ## Quickstart
60
+
61
+ ```python
62
+ from morphsdk import Morph
63
+
64
+ morph = Morph(api_key="sk-...") # or set MORPH_API_KEY
65
+
66
+ result = morph.edit.file(
67
+ path="src/app.py",
68
+ instruction="Add a retry decorator to fetch_user",
69
+ code_edit="# ... existing code ...\n@retry(max_attempts=3)\ndef fetch_user(user_id):",
70
+ )
71
+ print(result.changes) # lines added / removed / modified
72
+ ```
73
+
74
+ The API key is read from `MORPH_API_KEY` if you do not pass `api_key=`. Get one at [morphllm.com](https://morphllm.com).
75
+
76
+ ## Fast Apply: merge edits onto a file
77
+
78
+ `edit.file` reads the file, sends the original plus your edit snippet to the merge API, and writes the result back. Use `auto_write=False` to get the merged text without touching disk.
79
+
80
+ ```python
81
+ result = morph.edit.file(
82
+ path="src/app.py",
83
+ instruction="Rename the handler to on_request",
84
+ code_edit="# ... existing code ...\ndef on_request(req):\n # ... existing code ...",
85
+ )
86
+
87
+ # Code-in, code-out, no file I/O:
88
+ merged = morph.edit.code(
89
+ instruction="Add type hints",
90
+ original_code="def add(a, b):\n return a + b",
91
+ code_edit="def add(a: int, b: int) -> int:\n # ... existing code ...",
92
+ )
93
+ ```
94
+
95
+ ## WarpGrep: search a codebase
96
+
97
+ `grep.search` runs the WarpGrep agent loop over a local repo. Pass `stream=True` to iterate per-turn steps before the final result. `search_github` does the same against a public repo.
98
+
99
+ ```python
100
+ result = morph.grep.search(
101
+ query="where is the auth middleware registered?",
102
+ repo_root="/path/to/repo",
103
+ )
104
+
105
+ for step in morph.grep.search(query="rate limiter", repo_root="/path/to/repo", stream=True):
106
+ print(step)
107
+
108
+ result = morph.grep.search_github(
109
+ query="how does the router pick a model?",
110
+ github="morphllm/landing",
111
+ )
112
+ ```
113
+
114
+ ## Explore: multi-turn codebase Q&A
115
+
116
+ `explore.run` answers a natural-language question about a repository and returns a summary plus the contexts it found. `thoroughness` is `quick`, `medium`, or `thorough`.
117
+
118
+ ```python
119
+ result = morph.explore.run(
120
+ query="how does the WarpGrep agent loop call the model each turn?",
121
+ repo_root="/path/to/repo",
122
+ thoroughness="quick",
123
+ )
124
+ print(result.summary)
125
+ for ctx in result.contexts:
126
+ print(ctx)
127
+ ```
128
+
129
+ ## Compact: compress context
130
+
131
+ ```python
132
+ result = morph.compact(
133
+ input="...long transcript or code...",
134
+ query="what matters for the next step",
135
+ compression_ratio=0.5,
136
+ )
137
+ print(result)
138
+ ```
139
+
140
+ ## Router: pick a model for a prompt
141
+
142
+ ```python
143
+ choice = morph.router.select_model(input="Explain quicksort")
144
+ print(choice)
145
+ ```
146
+
147
+ ## More resources
148
+
149
+ The same client exposes `morph.search` (semantic codebase search by `repo_id`), `morph.browser` (`browser.run(task=..., url=...)`), `morph.git`, `morph.github`, `morph.mobile`, and `morph.reflex` (per-turn classifiers, OpenAI-fine-tuning-compatible). See the [docs](https://docs.morphllm.com) for the full surface.
150
+
151
+ ## Async
152
+
153
+ `AsyncMorph` mirrors `Morph` with every method as `async def`; streaming methods return an `AsyncIterator`.
154
+
155
+ ```python
156
+ import asyncio
157
+ from morphsdk import AsyncMorph
158
+
159
+ async def main():
160
+ async with AsyncMorph(api_key="sk-...") as morph:
161
+ result = await morph.edit.file(
162
+ path="src/app.py",
163
+ instruction="Fix the off-by-one",
164
+ code_edit="# ... existing code ...",
165
+ )
166
+ print(result.changes)
167
+
168
+ asyncio.run(main())
169
+ ```
170
+
171
+ ## Framework adapters
172
+
173
+ Install the extra for your agent framework to get ready-made tool definitions.
174
+
175
+ ```bash
176
+ pip install "morphsdk[openai]" # OpenAI tool schemas
177
+ pip install "morphsdk[anthropic]" # Anthropic tool schemas
178
+ pip install "morphsdk[langchain]" # LangChain tools
179
+ pip install "morphsdk[otel]" # OpenTelemetry tracing
180
+ pip install "morphsdk[all]" # everything above
181
+ ```
182
+
183
+ ```python
184
+ from morphsdk.adapters.openai import edit_file_tool_def
185
+
186
+ tools = [edit_file_tool_def()]
187
+ ```
188
+
189
+ ## Errors
190
+
191
+ Every failure raises a subclass of `MorphError`, so you can catch one type or branch on specifics.
192
+
193
+ ```python
194
+ from morphsdk import (
195
+ MorphError,
196
+ AuthenticationError,
197
+ RateLimitError,
198
+ ValidationError,
199
+ NotFoundError,
200
+ PermissionDeniedError,
201
+ APIConnectionError,
202
+ APITimeoutError,
203
+ )
204
+
205
+ try:
206
+ morph.edit.file(path="src/app.py", instruction="...", code_edit="...")
207
+ except RateLimitError:
208
+ ... # back off and retry
209
+ except MorphError as err:
210
+ ... # everything else
211
+ ```
212
+
213
+ ## Configuration
214
+
215
+ `Morph(...)` and `AsyncMorph(...)` accept:
216
+
217
+ | Argument | Default | Purpose |
218
+ | --- | --- | --- |
219
+ | `api_key` | `MORPH_API_KEY` | API key. Required. |
220
+ | `timeout` | `30.0` | Per-request timeout in seconds. |
221
+ | `max_retries` | `3` | Retries on transient failures. |
222
+ | `debug` | `False` | Log requests to stderr (`MORPH_DEBUG=1`). |
223
+
224
+ ## License
225
+
226
+ MIT
@@ -0,0 +1,176 @@
1
+ # Morph Python SDK
2
+
3
+ One typed client for Morph's specialized agent models: merge code edits with Fast Apply at ~10,500 tok/s, search a repo with WarpGrep, compress context with the compactor, route between models, and run browser tasks. Synchronous and async, Python 3.9+.
4
+
5
+ ```bash
6
+ pip install morphsdk
7
+ ```
8
+
9
+ ## Quickstart
10
+
11
+ ```python
12
+ from morphsdk import Morph
13
+
14
+ morph = Morph(api_key="sk-...") # or set MORPH_API_KEY
15
+
16
+ result = morph.edit.file(
17
+ path="src/app.py",
18
+ instruction="Add a retry decorator to fetch_user",
19
+ code_edit="# ... existing code ...\n@retry(max_attempts=3)\ndef fetch_user(user_id):",
20
+ )
21
+ print(result.changes) # lines added / removed / modified
22
+ ```
23
+
24
+ The API key is read from `MORPH_API_KEY` if you do not pass `api_key=`. Get one at [morphllm.com](https://morphllm.com).
25
+
26
+ ## Fast Apply: merge edits onto a file
27
+
28
+ `edit.file` reads the file, sends the original plus your edit snippet to the merge API, and writes the result back. Use `auto_write=False` to get the merged text without touching disk.
29
+
30
+ ```python
31
+ result = morph.edit.file(
32
+ path="src/app.py",
33
+ instruction="Rename the handler to on_request",
34
+ code_edit="# ... existing code ...\ndef on_request(req):\n # ... existing code ...",
35
+ )
36
+
37
+ # Code-in, code-out, no file I/O:
38
+ merged = morph.edit.code(
39
+ instruction="Add type hints",
40
+ original_code="def add(a, b):\n return a + b",
41
+ code_edit="def add(a: int, b: int) -> int:\n # ... existing code ...",
42
+ )
43
+ ```
44
+
45
+ ## WarpGrep: search a codebase
46
+
47
+ `grep.search` runs the WarpGrep agent loop over a local repo. Pass `stream=True` to iterate per-turn steps before the final result. `search_github` does the same against a public repo.
48
+
49
+ ```python
50
+ result = morph.grep.search(
51
+ query="where is the auth middleware registered?",
52
+ repo_root="/path/to/repo",
53
+ )
54
+
55
+ for step in morph.grep.search(query="rate limiter", repo_root="/path/to/repo", stream=True):
56
+ print(step)
57
+
58
+ result = morph.grep.search_github(
59
+ query="how does the router pick a model?",
60
+ github="morphllm/landing",
61
+ )
62
+ ```
63
+
64
+ ## Explore: multi-turn codebase Q&A
65
+
66
+ `explore.run` answers a natural-language question about a repository and returns a summary plus the contexts it found. `thoroughness` is `quick`, `medium`, or `thorough`.
67
+
68
+ ```python
69
+ result = morph.explore.run(
70
+ query="how does the WarpGrep agent loop call the model each turn?",
71
+ repo_root="/path/to/repo",
72
+ thoroughness="quick",
73
+ )
74
+ print(result.summary)
75
+ for ctx in result.contexts:
76
+ print(ctx)
77
+ ```
78
+
79
+ ## Compact: compress context
80
+
81
+ ```python
82
+ result = morph.compact(
83
+ input="...long transcript or code...",
84
+ query="what matters for the next step",
85
+ compression_ratio=0.5,
86
+ )
87
+ print(result)
88
+ ```
89
+
90
+ ## Router: pick a model for a prompt
91
+
92
+ ```python
93
+ choice = morph.router.select_model(input="Explain quicksort")
94
+ print(choice)
95
+ ```
96
+
97
+ ## More resources
98
+
99
+ The same client exposes `morph.search` (semantic codebase search by `repo_id`), `morph.browser` (`browser.run(task=..., url=...)`), `morph.git`, `morph.github`, `morph.mobile`, and `morph.reflex` (per-turn classifiers, OpenAI-fine-tuning-compatible). See the [docs](https://docs.morphllm.com) for the full surface.
100
+
101
+ ## Async
102
+
103
+ `AsyncMorph` mirrors `Morph` with every method as `async def`; streaming methods return an `AsyncIterator`.
104
+
105
+ ```python
106
+ import asyncio
107
+ from morphsdk import AsyncMorph
108
+
109
+ async def main():
110
+ async with AsyncMorph(api_key="sk-...") as morph:
111
+ result = await morph.edit.file(
112
+ path="src/app.py",
113
+ instruction="Fix the off-by-one",
114
+ code_edit="# ... existing code ...",
115
+ )
116
+ print(result.changes)
117
+
118
+ asyncio.run(main())
119
+ ```
120
+
121
+ ## Framework adapters
122
+
123
+ Install the extra for your agent framework to get ready-made tool definitions.
124
+
125
+ ```bash
126
+ pip install "morphsdk[openai]" # OpenAI tool schemas
127
+ pip install "morphsdk[anthropic]" # Anthropic tool schemas
128
+ pip install "morphsdk[langchain]" # LangChain tools
129
+ pip install "morphsdk[otel]" # OpenTelemetry tracing
130
+ pip install "morphsdk[all]" # everything above
131
+ ```
132
+
133
+ ```python
134
+ from morphsdk.adapters.openai import edit_file_tool_def
135
+
136
+ tools = [edit_file_tool_def()]
137
+ ```
138
+
139
+ ## Errors
140
+
141
+ Every failure raises a subclass of `MorphError`, so you can catch one type or branch on specifics.
142
+
143
+ ```python
144
+ from morphsdk import (
145
+ MorphError,
146
+ AuthenticationError,
147
+ RateLimitError,
148
+ ValidationError,
149
+ NotFoundError,
150
+ PermissionDeniedError,
151
+ APIConnectionError,
152
+ APITimeoutError,
153
+ )
154
+
155
+ try:
156
+ morph.edit.file(path="src/app.py", instruction="...", code_edit="...")
157
+ except RateLimitError:
158
+ ... # back off and retry
159
+ except MorphError as err:
160
+ ... # everything else
161
+ ```
162
+
163
+ ## Configuration
164
+
165
+ `Morph(...)` and `AsyncMorph(...)` accept:
166
+
167
+ | Argument | Default | Purpose |
168
+ | --- | --- | --- |
169
+ | `api_key` | `MORPH_API_KEY` | API key. Required. |
170
+ | `timeout` | `30.0` | Per-request timeout in seconds. |
171
+ | `max_retries` | `3` | Retries on transient failures. |
172
+ | `debug` | `False` | Log requests to stderr (`MORPH_DEBUG=1`). |
173
+
174
+ ## License
175
+
176
+ MIT
@@ -0,0 +1,54 @@
1
+ """Morph SDK -- AI-powered code editing, search, browser automation, and more.
2
+
3
+ Usage::
4
+
5
+ from morphsdk import Morph
6
+
7
+ morph = Morph(api_key="sk-...")
8
+
9
+ # Edit files
10
+ result = morph.edit.file(path="app.py", instruction="Fix bug", code_edit="...")
11
+
12
+ # Search code
13
+ result = morph.search.code(query="authentication", repo_id="my-project")
14
+
15
+ # Browser automation
16
+ result = morph.browser.run(task="Test login", url="https://app.example.com")
17
+
18
+ # Context compression
19
+ result = morph.compact(input="Long text to compress...")
20
+
21
+ # Model routing
22
+ result = morph.router.select_model(input="Explain quicksort")
23
+ """
24
+
25
+ from ._client import AsyncMorph, Morph
26
+ from ._errors import (
27
+ APIConnectionError,
28
+ APITimeoutError,
29
+ AuthenticationError,
30
+ InternalError,
31
+ MorphError,
32
+ NotFoundError,
33
+ PermissionDeniedError,
34
+ RateLimitError,
35
+ ValidationError,
36
+ )
37
+ from ._version import __version__
38
+
39
+ __all__ = [
40
+ "__version__",
41
+ # Clients
42
+ "Morph",
43
+ "AsyncMorph",
44
+ # Errors
45
+ "MorphError",
46
+ "AuthenticationError",
47
+ "PermissionDeniedError",
48
+ "NotFoundError",
49
+ "RateLimitError",
50
+ "ValidationError",
51
+ "APIConnectionError",
52
+ "APITimeoutError",
53
+ "InternalError",
54
+ ]
@@ -0,0 +1,64 @@
1
+ """Agent orchestration internals (WarpGrep multi-turn loop, Explore subagent).
2
+
3
+ The WarpGrep agent core is **async** -- it awaits the async providers and a
4
+ single ``httpx`` chat-completions call per turn. ``run_warp_grep`` /
5
+ ``run_warp_grep_streaming`` are the public entry points consumed by the sync
6
+ ``GrepResource`` (via :func:`asyncio.run` / a thread bridge) and, in a later
7
+ wave, directly by the async client.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from .explore import (
13
+ DEFAULT_MAX_TURNS as EXPLORE_DEFAULT_MAX_TURNS,
14
+ )
15
+ from .explore import (
16
+ DEFAULT_THOROUGHNESS,
17
+ ExploreContext,
18
+ ExploreMessageEvent,
19
+ ExploreRunResult,
20
+ ExploreStepEvent,
21
+ ExploreThoroughness,
22
+ run_explore,
23
+ run_explore_streaming,
24
+ )
25
+ from .runner import (
26
+ TOOL_SPECS,
27
+ call_model,
28
+ run_warp_grep,
29
+ run_warp_grep_streaming,
30
+ )
31
+ from .types import (
32
+ AgentFinish,
33
+ AgentRunResult,
34
+ ChatMessage,
35
+ FinishFileSpec,
36
+ ResolvedContext,
37
+ ToolCallRef,
38
+ WarpGrepExecutionMetrics,
39
+ WarpGrepStep,
40
+ )
41
+
42
+ __all__ = [
43
+ "run_warp_grep",
44
+ "run_warp_grep_streaming",
45
+ "call_model",
46
+ "TOOL_SPECS",
47
+ "run_explore",
48
+ "run_explore_streaming",
49
+ "ExploreRunResult",
50
+ "ExploreStepEvent",
51
+ "ExploreMessageEvent",
52
+ "ExploreContext",
53
+ "ExploreThoroughness",
54
+ "EXPLORE_DEFAULT_MAX_TURNS",
55
+ "DEFAULT_THOROUGHNESS",
56
+ "AgentRunResult",
57
+ "AgentFinish",
58
+ "ChatMessage",
59
+ "FinishFileSpec",
60
+ "ResolvedContext",
61
+ "ToolCallRef",
62
+ "WarpGrepStep",
63
+ "WarpGrepExecutionMetrics",
64
+ ]
@@ -0,0 +1,52 @@
1
+ """Agent loop configuration constants.
2
+
3
+ Cross-checked against the TypeScript ``AGENT_CONFIG`` (``agent/config.ts``).
4
+ Every value here matches ``_constants.py`` **except the timeout**: the TS default
5
+ is 60_000 ms, while ``_constants.WARP_GREP_TIMEOUT`` is 30.0 s. The TS value is
6
+ authoritative for the agent loop, so we override it module-locally here (the
7
+ shared ``_constants`` module is owned elsewhere and intentionally left untouched).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+
14
+ from morphsdk._constants import (
15
+ WARP_GREP_MAX_CONTEXT_CHARS,
16
+ WARP_GREP_MAX_LIST_DEPTH,
17
+ WARP_GREP_MAX_LIST_RESULTS,
18
+ WARP_GREP_MAX_OUTPUT_LINES,
19
+ WARP_GREP_MAX_READ_LINES,
20
+ WARP_GREP_MAX_TURNS,
21
+ WARP_GREP_MODEL,
22
+ )
23
+
24
+ DEFAULT_MODEL = WARP_GREP_MODEL
25
+ MAX_TURNS = WARP_GREP_MAX_TURNS
26
+ MAX_CONTEXT_CHARS = WARP_GREP_MAX_CONTEXT_CHARS
27
+ MAX_OUTPUT_LINES = WARP_GREP_MAX_OUTPUT_LINES
28
+ MAX_LIST_RESULTS = WARP_GREP_MAX_LIST_RESULTS
29
+ MAX_READ_LINES = WARP_GREP_MAX_READ_LINES
30
+ MAX_LIST_DEPTH = WARP_GREP_MAX_LIST_DEPTH
31
+
32
+ # TS AGENT_CONFIG.TIMEOUT_MS default is 60_000 ms (overridable via env). We expose
33
+ # it in **seconds** to match the rest of the Python SDK's timeout convention.
34
+ _DEFAULT_TIMEOUT_S = 60.0
35
+
36
+
37
+ def _resolve_timeout_s() -> float:
38
+ """Mirror TS ``parseEnvTimeout(MORPH_WARP_GREP_TIMEOUT, 60_000)`` (ms env -> s)."""
39
+ raw = os.environ.get("MORPH_WARP_GREP_TIMEOUT")
40
+ if not raw:
41
+ return _DEFAULT_TIMEOUT_S
42
+ try:
43
+ ms = int(raw)
44
+ except ValueError:
45
+ return _DEFAULT_TIMEOUT_S
46
+ return ms / 1000.0 if ms > 0 else _DEFAULT_TIMEOUT_S
47
+
48
+
49
+ DEFAULT_TIMEOUT_S = _resolve_timeout_s()
50
+
51
+ # Default code-search host for GitHub repo resolution + code-storage commands.
52
+ DEFAULT_CODE_SEARCH_URL = "https://morphllm.com"