Open
Conversation
New package `unstract.prompt_studio` with `PromptStudioClient` for automating Prompt Studio project promotion across environments using Platform API Key (Bearer token) authentication. Core methods: - list_projects, get_project, export_project, import_project - sync_prompts, create_profile, export_tool, upload_file - check_deployment_usage, get_default_triad High-level orchestration: - promote(): export → import/sync → optional export for deployment create_profile falls back to user's default triad when adapter IDs are not explicitly provided. Includes 15 unit tests covering all methods and promotion flows.
Greptile SummaryThis PR introduces a new Two issues remain:
Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant Source as PromptStudioClient (source)
participant Target as PromptStudioClient (target)
participant SourceAPI as Source API
participant TargetAPI as Target API
Note over User,TargetAPI: One-time setup (import_project + create_profile)
User->>Target: import_project(export_data, adapters)
Target->>TargetAPI: POST /prompt-studio/project-transfer/
TargetAPI-->>Target: {tool_id, needs_adapter_config}
Target-->>User: result
User->>Target: create_profile(tool_id, llm, ...)
alt adapter IDs missing
Target->>TargetAPI: GET /adapter/default_triad/
TargetAPI-->>Target: {default_llm_adapter, ...}
end
Target->>TargetAPI: POST /prompt-studio/profilemanager/{tool_id}
TargetAPI-->>Target: created profile
Target-->>User: profile dict
Note over User,TargetAPI: Ongoing promotion (promote)
User->>Source: promote(tool_id, target, target_tool_id, export_as_tool)
Source->>SourceAPI: GET /prompt-studio/project-transfer/{tool_id}
SourceAPI-->>Source: export_data (tool_metadata, prompts, ...)
Source->>Target: sync_prompts(target_tool_id, export_data, create_copy)
Target->>TargetAPI: POST /prompt-studio/{target_tool_id}/sync-prompts/
TargetAPI-->>Target: {prompts_created, prompts_deleted, backup_tool_id?}
alt export_as_tool=True
Source->>Target: export_tool(target_tool_id)
Target->>TargetAPI: POST /prompt-studio/export/{target_tool_id}
TargetAPI-->>Target: {status: exported}
end
Source-->>User: {tool_id, prompts_created, export_result?}
Prompt To Fix All With AIThis is a comment left during a code review.
Path: src/unstract/prompt_studio/client.py
Line: 35
Comment:
**Unused `os` import**
`os` is imported on this line but is never referenced anywhere in the file. This will produce a lint warning and can be safely removed.
```suggestion
import json
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: src/unstract/prompt_studio/client.py
Line: 186-198
Comment:
**String path that doesn't exist silently becomes file content**
When `export_data` is a `str` that looks like a file path but the file does **not** exist, the condition on line 186 (`isinstance(export_data, str) and Path(export_data).is_file()`) evaluates to `False`. Control then falls through to the final `elif isinstance(export_data, str)` branch (line 196), which encodes the *path string itself* as the file content and POSTs it to the server.
For example:
```python
client.import_project("/tmp/nonexistent_export.json")
# → POSTs the literal bytes of "/tmp/nonexistent_export.json" as the JSON file
```
The previous thread addressed the `Path` case (which now raises `FileNotFoundError`), but the `str` case for non-existent paths is still silently mishandled. The user will receive a confusing server-side JSON parse error rather than a clear `FileNotFoundError`.
Consider mirroring the `Path` fix:
```python
elif isinstance(export_data, str) and Path(export_data).is_file():
with open(export_data, "rb") as f:
content = f.read()
filename = Path(export_data).name
elif isinstance(export_data, str) and os.sep in export_data:
# Looks like a path but file doesn't exist
raise FileNotFoundError(f"Export file not found: {export_data}")
elif isinstance(export_data, dict):
...
elif isinstance(export_data, str):
content = export_data.encode()
filename = "export.json"
```
Or, more simply, check if the string *looks* like a path (e.g., ends in `.json`) before falling back to treating it as a raw JSON string.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: tests/test_prompt_studio.py
Line: 1-10
Comment:
**Missing test coverage for several methods**
The PR description states "14 unit tests covering all methods," but the following public methods have no test at all:
- `get_project(tool_id)`
- `check_deployment_usage(tool_id)`
- `get_default_triad()`
- `create_profile(tool_id, ...)` — the most complex method in the client, with the default-triad fallback logic
- `upload_file(tool_id, file_path)`
Additionally, the `import_project` test suite doesn't cover the raw-JSON-string input path (`isinstance(export_data, str)` final branch). Given that `create_profile` fetches the default triad if any adapter ID is missing and then validates completeness, this method in particular would benefit from tests covering: (a) all adapters supplied, (b) partial adapters supplemented by default triad, and (c) missing adapters with no default triad configured (should raise `PromptStudioClientError`).
How can I resolve this? If you propose a fix, please make it concise.Reviews (5): Last reviewed commit: "Rename export to export_as_tool in promo..." | Re-trigger Greptile |
promote() now requires target_tool_id — it only syncs into an existing project. Fresh import is a separate one-time setup step via import_project() + create_profile().
…ault - Fix file handle leaks in import_project and upload_file by reading eagerly into bytes instead of passing open file handles - Export PromptStudioClientError from package __init__ - Fix header dict mutation in _request by merging into a new dict - Expose is_default parameter in create_profile (default True) - Remove unused io import
- Let caller-supplied headers override defaults (auth as base, not top) - Raise FileNotFoundError for Path objects that don't exist instead of falling through to generic type error - Separate Path vs str-as-path handling for clarity
chandrasekharan-zipstack
approved these changes
Mar 19, 2026
hari-kuriakose
left a comment
There was a problem hiding this comment.
@Deepak-Kesavan LGTM overall.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
unstract.prompt_studiopackage withPromptStudioClientfor automating Prompt Studio project promotion across environments via Platform API Key (Bearer token) auth.Methods
list_projects()get_project(tool_id)export_project(tool_id)import_project(export_data, adapters)sync_prompts(tool_id, export_data, create_copy)create_profile(tool_id, ...)export_tool(tool_id)upload_file(tool_id, file_path)check_deployment_usage(tool_id)get_default_triad()promote(tool_id, target, ...)Usage
One-time setup on target:
Ongoing promotion:
Test plan