tamarind-cli 0.2.0__tar.gz → 0.3.0__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 (97) hide show
  1. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/.github/workflows/ci.yml +35 -0
  2. tamarind_cli-0.2.0/README.md → tamarind_cli-0.3.0/PKG-INFO +59 -0
  3. tamarind_cli-0.2.0/PKG-INFO → tamarind_cli-0.3.0/README.md +34 -21
  4. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/docs/architecture.md +39 -2
  5. tamarind_cli-0.3.0/openapi/openapi-python-client.json +4 -0
  6. tamarind_cli-0.3.0/openapi/public-v1.json +1820 -0
  7. tamarind_cli-0.3.0/openapi/public-v1.lock.json +8 -0
  8. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/pyproject.toml +5 -1
  9. tamarind_cli-0.3.0/scripts/check_custom_tools_provenance.py +40 -0
  10. tamarind_cli-0.3.0/scripts/sync_custom_tools_contract.py +347 -0
  11. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/__init__.py +8 -1
  12. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/config.py +4 -5
  13. tamarind_cli-0.3.0/src/tamarind/custom_tools/__init__.py +31 -0
  14. tamarind_cli-0.3.0/src/tamarind/custom_tools/_contract.py +3 -0
  15. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/__init__.py +8 -0
  16. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/__init__.py +1 -0
  17. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/__init__.py +1 -0
  18. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/build_custom_tool_version.py +220 -0
  19. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/cancel_custom_tool_build.py +213 -0
  20. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/create_custom_tool.py +185 -0
  21. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/create_custom_tool_upload.py +203 -0
  22. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/delete_custom_tool.py +205 -0
  23. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/get_custom_tool.py +177 -0
  24. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/get_custom_tool_version.py +205 -0
  25. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/list_custom_tool_build_logs.py +239 -0
  26. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/list_custom_tool_versions.py +263 -0
  27. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/list_custom_tools.py +252 -0
  28. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/publish_custom_tool_version.py +213 -0
  29. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/api/custom_tools/update_custom_tool.py +233 -0
  30. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/client.py +272 -0
  31. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/errors.py +16 -0
  32. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/__init__.py +57 -0
  33. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_build_error.py +50 -0
  34. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_build_event.py +50 -0
  35. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_build_log_page.py +110 -0
  36. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_build_result.py +59 -0
  37. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_build_result_action.py +10 -0
  38. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_create_custom_tool_request.py +109 -0
  39. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_create_custom_tool_request_gputype.py +13 -0
  40. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_create_custom_tool_request_memory.py +16 -0
  41. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_create_version_request.py +50 -0
  42. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_custom_tool.py +242 -0
  43. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_custom_tool_gputype.py +13 -0
  44. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_custom_tool_memory.py +16 -0
  45. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_custom_tool_page.py +70 -0
  46. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_custom_tool_status.py +10 -0
  47. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_problem.py +137 -0
  48. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_problem_errors_type_0_item.py +47 -0
  49. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_update_custom_tool_request.py +304 -0
  50. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_update_custom_tool_request_gpu_type_type_0.py +13 -0
  51. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_update_custom_tool_request_memory_type_0.py +16 -0
  52. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_upload_session.py +91 -0
  53. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_upload_session_uploadheaders.py +47 -0
  54. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_version.py +160 -0
  55. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_version_page.py +70 -0
  56. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/models/public_version_status.py +11 -0
  57. tamarind_cli-0.3.0/src/tamarind/custom_tools/_generated/types.py +54 -0
  58. tamarind_cli-0.3.0/src/tamarind/custom_tools/client.py +36 -0
  59. tamarind_cli-0.3.0/src/tamarind/custom_tools/packaging.py +584 -0
  60. tamarind_cli-0.3.0/src/tamarind/custom_tools/resources.py +755 -0
  61. tamarind_cli-0.3.0/src/tamarind/custom_tools/transport.py +319 -0
  62. tamarind_cli-0.3.0/src/tamarind/custom_tools/validation.py +146 -0
  63. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/errors.py +48 -0
  64. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/http.py +121 -20
  65. tamarind_cli-0.3.0/src/tamarind/py.typed +0 -0
  66. tamarind_cli-0.3.0/tests/test_custom_tools_generation.py +314 -0
  67. tamarind_cli-0.3.0/tests/test_custom_tools_packaging.py +744 -0
  68. tamarind_cli-0.3.0/tests/test_custom_tools_sdk.py +1005 -0
  69. tamarind_cli-0.3.0/tests/test_pep561.py +5 -0
  70. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_rest.py +120 -17
  71. tamarind_cli-0.3.0/uv.lock +1018 -0
  72. tamarind_cli-0.2.0/uv.lock +0 -406
  73. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/.github/workflows/publish.yml +0 -0
  74. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/.gitignore +0 -0
  75. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/LICENSE +0 -0
  76. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/install.sh +0 -0
  77. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/catalog.py +0 -0
  78. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/__init__.py +0 -0
  79. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/commands/__init__.py +0 -0
  80. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/commands/auth.py +0 -0
  81. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/commands/catalog.py +0 -0
  82. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/commands/files.py +0 -0
  83. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/commands/jobs.py +0 -0
  84. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/guidance.py +0 -0
  85. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/inputs.py +0 -0
  86. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/main.py +0 -0
  87. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/cli/output.py +0 -0
  88. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/jobs.py +0 -0
  89. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/src/tamarind/rest.py +0 -0
  90. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_agent_contract.py +0 -0
  91. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_bugbash.py +0 -0
  92. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_cli_commands.py +0 -0
  93. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_config.py +0 -0
  94. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_file_filters.py +0 -0
  95. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_inputs.py +0 -0
  96. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_jobs.py +0 -0
  97. {tamarind_cli-0.2.0 → tamarind_cli-0.3.0}/tests/test_output.py +0 -0
@@ -9,6 +9,41 @@ permissions:
9
9
  contents: read
10
10
 
11
11
  jobs:
12
+ generated:
13
+ name: Generated transport is current
14
+ runs-on: ubuntu-latest
15
+ timeout-minutes: 5
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v5
19
+ - name: Verify backend provenance and generated client freshness
20
+ run: |
21
+ uv run --extra dev python scripts/check_custom_tools_provenance.py
22
+ tmp_dir=$(mktemp -d .custom-tools-generated.XXXXXX)
23
+ trap 'rm -rf "$tmp_dir"' EXIT
24
+ uv run --extra dev openapi-python-client generate --path openapi/public-v1.json \
25
+ --config openapi/openapi-python-client.json --meta none \
26
+ --output-path "$tmp_dir/generated" --overwrite
27
+ diff -ru --exclude=.ruff_cache src/tamarind/custom_tools/_generated "$tmp_dir/generated"
28
+
29
+ typecheck:
30
+ name: Custom Tools type check
31
+ runs-on: ubuntu-latest
32
+ timeout-minutes: 5
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: astral-sh/setup-uv@v5
36
+ with:
37
+ enable-cache: true
38
+ python-version: "3.12"
39
+ - name: Check the public SDK and transport boundary
40
+ run: >-
41
+ uv run --extra dev mypy
42
+ src/tamarind/custom_tools
43
+ src/tamarind/http.py
44
+ src/tamarind/errors.py
45
+ scripts/sync_custom_tools_contract.py
46
+
12
47
  package:
13
48
  name: Build release artifacts
14
49
  runs-on: ubuntu-latest
@@ -1,3 +1,28 @@
1
+ Metadata-Version: 2.5
2
+ Name: tamarind-cli
3
+ Version: 0.3.0
4
+ Summary: Command-line interface for the Tamarind Bio platform — submit, monitor, and download protein/molecule jobs from your terminal or an AI agent.
5
+ Project-URL: Homepage, https://tamarind.bio
6
+ Project-URL: Documentation, https://app.tamarind.bio/api-docs
7
+ Project-URL: Source, https://github.com/Tamarind-Bio/tamarind-cli
8
+ Author: Tamarind Bio
9
+ License: Apache-2.0
10
+ License-File: LICENSE
11
+ Keywords: agents,alphafold,bioinformatics,boltz,cli,protein
12
+ Requires-Python: >=3.10
13
+ Requires-Dist: attrs>=23.1
14
+ Requires-Dist: httpx>=0.27
15
+ Requires-Dist: pyyaml>=6.0
16
+ Requires-Dist: typer>=0.12
17
+ Requires-Dist: typing-extensions>=4.7
18
+ Provides-Extra: dev
19
+ Requires-Dist: mypy>=1.11; extra == 'dev'
20
+ Requires-Dist: openapi-python-client==0.28.4; extra == 'dev'
21
+ Requires-Dist: pytest>=8.0; extra == 'dev'
22
+ Requires-Dist: respx>=0.21; extra == 'dev'
23
+ Requires-Dist: ruff>=0.5; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
1
26
  # Tamarind CLI
2
27
 
3
28
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
@@ -78,6 +103,40 @@ tamarind submit boltz \
78
103
  --name quick-fold
79
104
  ```
80
105
 
106
+ ## Python SDK
107
+
108
+ The package also exposes a typed Custom Tools SDK. It checks archive-local
109
+ safety, packages a folder, uploads it directly to object storage, and submits the
110
+ digest-checked build request. The server
111
+ remains authoritative for source readiness, Versions, logs, and the evolving
112
+ `config.json` contract.
113
+
114
+ ```python
115
+ from tamarind import Tamarind
116
+ from tamarind.errors import CustomToolNotFoundError
117
+
118
+ with Tamarind() as client:
119
+ try:
120
+ tool = client.custom_tools.get("my-esmfold")
121
+ except CustomToolNotFoundError:
122
+ tool = client.custom_tools.create("my-esmfold", display_name="My ESMFold")
123
+
124
+ result = tool.build("./my-esmfold")
125
+ print(result.action) # build, reuse_image, or unchanged
126
+ version = result.version
127
+ if not version.terminal:
128
+ version = version.monitor(timeout=1800, on_event=print)
129
+ ```
130
+
131
+ `build()` returns a typed result describing what the request did and the durable
132
+ Version it produced. It is convenience orchestration, not a durable request
133
+ object. If the connection is lost during the build response, fetch the tool's
134
+ versions before retrying. Interrupting `monitor()` stops local monitoring; it
135
+ does not cancel the remote build.
136
+
137
+ Custom Tool CLI commands are not part of this release. Existing CLI commands
138
+ remain unchanged.
139
+
81
140
  ## Output for agents
82
141
 
83
142
  Every command emits JSON when stdout is not a TTY, or with `--json`. Result
@@ -1,24 +1,3 @@
1
- Metadata-Version: 2.4
2
- Name: tamarind-cli
3
- Version: 0.2.0
4
- Summary: Command-line interface for the Tamarind Bio platform — submit, monitor, and download protein/molecule jobs from your terminal or an AI agent.
5
- Project-URL: Homepage, https://tamarind.bio
6
- Project-URL: Documentation, https://app.tamarind.bio/api-docs
7
- Project-URL: Source, https://github.com/Tamarind-Bio/tamarind-cli
8
- Author: Tamarind Bio
9
- License: Apache-2.0
10
- License-File: LICENSE
11
- Keywords: agents,alphafold,bioinformatics,boltz,cli,protein
12
- Requires-Python: >=3.10
13
- Requires-Dist: httpx>=0.27
14
- Requires-Dist: pyyaml>=6.0
15
- Requires-Dist: typer>=0.12
16
- Provides-Extra: dev
17
- Requires-Dist: pytest>=8.0; extra == 'dev'
18
- Requires-Dist: respx>=0.21; extra == 'dev'
19
- Requires-Dist: ruff>=0.5; extra == 'dev'
20
- Description-Content-Type: text/markdown
21
-
22
1
  # Tamarind CLI
23
2
 
24
3
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
@@ -99,6 +78,40 @@ tamarind submit boltz \
99
78
  --name quick-fold
100
79
  ```
101
80
 
81
+ ## Python SDK
82
+
83
+ The package also exposes a typed Custom Tools SDK. It checks archive-local
84
+ safety, packages a folder, uploads it directly to object storage, and submits the
85
+ digest-checked build request. The server
86
+ remains authoritative for source readiness, Versions, logs, and the evolving
87
+ `config.json` contract.
88
+
89
+ ```python
90
+ from tamarind import Tamarind
91
+ from tamarind.errors import CustomToolNotFoundError
92
+
93
+ with Tamarind() as client:
94
+ try:
95
+ tool = client.custom_tools.get("my-esmfold")
96
+ except CustomToolNotFoundError:
97
+ tool = client.custom_tools.create("my-esmfold", display_name="My ESMFold")
98
+
99
+ result = tool.build("./my-esmfold")
100
+ print(result.action) # build, reuse_image, or unchanged
101
+ version = result.version
102
+ if not version.terminal:
103
+ version = version.monitor(timeout=1800, on_event=print)
104
+ ```
105
+
106
+ `build()` returns a typed result describing what the request did and the durable
107
+ Version it produced. It is convenience orchestration, not a durable request
108
+ object. If the connection is lost during the build response, fetch the tool's
109
+ versions before retrying. Interrupting `monitor()` stops local monitoring; it
110
+ does not cancel the remote build.
111
+
112
+ Custom Tool CLI commands are not part of this release. Existing CLI commands
113
+ remain unchanged.
114
+
102
115
  ## Output for agents
103
116
 
104
117
  Every command emits JSON when stdout is not a TTY, or with `--json`. Result
@@ -2,10 +2,10 @@
2
2
 
3
3
  The CLI, the [MCP server](https://mcp.tamarind.bio), and the web app are all
4
4
  **thin clients** over the same platform. The CLI never re-implements business
5
- logic; it only knows how to call two well-defined surfaces. This is what keeps
5
+ logic; it only knows how to call three well-defined surfaces. This is what keeps
6
6
  the CLI and the MCP from drifting as the platform evolves.
7
7
 
8
- ## Two surfaces, two single sources of truth
8
+ ## Three surfaces, three single sources of truth
9
9
 
10
10
  ### 1. Job/file REST surface — source of truth: the OpenAPI spec
11
11
 
@@ -37,6 +37,43 @@ Because the logic lives in one module, *where* discovery is hosted (the MCP host
37
37
  today; potentially the main API or a dedicated service later) is a deployment
38
38
  detail that can change without any client change and without drift.
39
39
 
40
+ ### 3. Custom Tools surface — source of truth: the public OpenAPI artifact
41
+
42
+ Custom Tool creation, source upload, build, Versions, logs, and cancellation
43
+ are generated from the website backend's dedicated public OpenAPI artifact:
44
+
45
+ ```text
46
+ backend-owned custom-tools-v1.generated.json
47
+ -> Git-object provenance verification
48
+ -> vendored spec + lock
49
+ -> openapi-python-client==0.28.4
50
+ -> generated models/endpoints
51
+ -> thin Tamarind transport and resource adapters
52
+ ```
53
+
54
+ The Website owns the projection and its referenced-component closure. The CLI sync
55
+ verifies the exact producer repository, commit, path, and bytes before atomically
56
+ installing the spec, provenance lock, generated server metadata, and generated package.
57
+ CI regenerates with the pinned mature generator and rejects drift. The handwritten
58
+ layer contains only HTTP/error normalization and ergonomic resource composition; it
59
+ does not parse OpenAPI or maintain a second schema IR/compiler.
60
+
61
+ The SDK owns archive-local concerns that only the client can decide safely:
62
+ deterministic ZIP construction, symlink and junction rejection, upload limits,
63
+ JSON parseability and top-level object shape, and warnings about the networkless
64
+ runtime. Archives use stored ZIP entries so source digests do not depend on a
65
+ platform zlib build. Modes are also platform-stable: `run.sh` and files beginning
66
+ with a shebang are executable, while all other files are regular. The backend owns
67
+ the evolving `config.json` business contract and
68
+ validates it before accepting a build. The SDK deliberately does not maintain a
69
+ second list of configuration fields, enums, or cross-field rules.
70
+
71
+ `CustomTool.build()` composes the existing requests: create upload, PUT the archive,
72
+ submit the digest-checked build request, and return a typed result containing the server's
73
+ action plus its durable Version. It adds no server-side
74
+ BuildRequest, queue, lease, claim, or repair state. An ambiguous build response
75
+ is therefore handled honestly by listing Versions before a manual retry.
76
+
40
77
  ## Why not a single binary that re-encodes the API?
41
78
 
42
79
  A from-scratch client in another language would re-encode the request shapes and
@@ -0,0 +1,4 @@
1
+ {
2
+ "package_name_override": "_generated",
3
+ "project_name_override": "tamarind-custom-tools-generated"
4
+ }