pixie-prompts 0.0.0__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.
- pixie/prompts/__init__.py +0 -0
- pixie/prompts/graphql.py +205 -0
- pixie/prompts/prompt.py +373 -0
- pixie/prompts/prompt_management.py +82 -0
- pixie/prompts/server.py +263 -0
- pixie/prompts/storage.py +228 -0
- pixie/tests/__init__.py +0 -0
- pixie/tests/test_prompt.py +1321 -0
- pixie/tests/test_prompt_management.py +117 -0
- pixie/tests/test_prompt_storage.py +1453 -0
- pixie_prompts-0.0.0.dist-info/METADATA +35 -0
- pixie_prompts-0.0.0.dist-info/RECORD +15 -0
- pixie_prompts-0.0.0.dist-info/WHEEL +4 -0
- pixie_prompts-0.0.0.dist-info/entry_points.txt +3 -0
- pixie_prompts-0.0.0.dist-info/licenses/LICENSE +21 -0
pixie/prompts/server.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""FastAPI server for SDK."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import colorlog
|
|
6
|
+
import logging
|
|
7
|
+
import sys
|
|
8
|
+
import importlib.util
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from urllib.parse import quote
|
|
11
|
+
|
|
12
|
+
import dotenv
|
|
13
|
+
from fastapi import FastAPI
|
|
14
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
15
|
+
from strawberry.fastapi import GraphQLRouter
|
|
16
|
+
import uvicorn
|
|
17
|
+
|
|
18
|
+
from pixie.prompts.storage import initialize_prompt_storage
|
|
19
|
+
from pixie.prompts.graphql import schema
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Global logging mode
|
|
25
|
+
_logging_mode: str = "default"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def discover_and_load_prompts():
|
|
29
|
+
"""Discover and load all Python files that use pixie.prompts.create_prompt, or pixie.create_prompt.
|
|
30
|
+
|
|
31
|
+
This function recursively searches the current working directory for Python files
|
|
32
|
+
"""
|
|
33
|
+
cwd = Path.cwd()
|
|
34
|
+
# Recursively find all Python files
|
|
35
|
+
python_files = list(cwd.rglob("*.py"))
|
|
36
|
+
|
|
37
|
+
if not python_files:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Add current directory to Python path if not already there
|
|
41
|
+
if str(cwd) not in sys.path:
|
|
42
|
+
sys.path.insert(0, str(cwd))
|
|
43
|
+
|
|
44
|
+
loaded_count = 0
|
|
45
|
+
for py_file in python_files:
|
|
46
|
+
# Skip __init__.py, private files, and anything in site-packages/venv
|
|
47
|
+
if py_file.name.startswith("_") or any(
|
|
48
|
+
part in py_file.parts
|
|
49
|
+
for part in ["site-packages", ".venv", "venv", "__pycache__"]
|
|
50
|
+
):
|
|
51
|
+
continue
|
|
52
|
+
|
|
53
|
+
# Load the module with a unique name based on path
|
|
54
|
+
relative_path = py_file.relative_to(cwd)
|
|
55
|
+
module_name = str(relative_path.with_suffix("")).replace("/", ".")
|
|
56
|
+
spec = importlib.util.spec_from_file_location(module_name, py_file)
|
|
57
|
+
if spec and spec.loader:
|
|
58
|
+
module = importlib.util.module_from_spec(spec)
|
|
59
|
+
sys.modules[module_name] = module
|
|
60
|
+
spec.loader.exec_module(module)
|
|
61
|
+
loaded_count += 1
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def setup_logging(mode: str = "default"):
|
|
65
|
+
"""Configure logging for the entire application.
|
|
66
|
+
|
|
67
|
+
Sets up colored logging with consistent formatting for all loggers.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
mode: Logging mode - "default", "verbose", or "debug"
|
|
71
|
+
- default: INFO for server events, WARNING+ for all modules
|
|
72
|
+
- verbose: INFO+ for all modules
|
|
73
|
+
- debug: DEBUG+ for all modules
|
|
74
|
+
"""
|
|
75
|
+
global _logging_mode
|
|
76
|
+
_logging_mode = mode
|
|
77
|
+
|
|
78
|
+
# Determine log level based on mode
|
|
79
|
+
if mode == "debug":
|
|
80
|
+
level = logging.DEBUG
|
|
81
|
+
elif mode == "verbose":
|
|
82
|
+
level = logging.INFO
|
|
83
|
+
else: # default
|
|
84
|
+
level = logging.INFO
|
|
85
|
+
|
|
86
|
+
colorlog.basicConfig(
|
|
87
|
+
level=level,
|
|
88
|
+
format="[%(log_color)s%(levelname)-8s%(reset)s][%(asctime)s]\t%(message)s",
|
|
89
|
+
datefmt="%H:%M:%S",
|
|
90
|
+
log_colors={
|
|
91
|
+
"DEBUG": "cyan",
|
|
92
|
+
"INFO": "green",
|
|
93
|
+
"WARNING": "yellow",
|
|
94
|
+
"ERROR": "red",
|
|
95
|
+
"CRITICAL": "red,bg_white",
|
|
96
|
+
},
|
|
97
|
+
force=True,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Configure uvicorn loggers to use the same format
|
|
101
|
+
for logger_name in ["uvicorn", "uvicorn.access", "uvicorn.error"]:
|
|
102
|
+
uvicorn_logger = logging.getLogger(logger_name)
|
|
103
|
+
uvicorn_logger.handlers = []
|
|
104
|
+
uvicorn_logger.propagate = True
|
|
105
|
+
|
|
106
|
+
# In default mode, set most loggers to WARNING+ except specific modules
|
|
107
|
+
if mode == "default":
|
|
108
|
+
# Set root logger to WARNING
|
|
109
|
+
logging.getLogger().setLevel(logging.WARNING)
|
|
110
|
+
# Allow INFO for pixie modules
|
|
111
|
+
logging.getLogger("pixie").setLevel(logging.INFO)
|
|
112
|
+
# Suppress uvicorn access logs in default mode
|
|
113
|
+
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def create_app() -> FastAPI:
|
|
117
|
+
"""Create and configure the FastAPI application.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Configured FastAPI application instance with GraphQL router.
|
|
121
|
+
"""
|
|
122
|
+
# Setup logging first (use global logging mode)
|
|
123
|
+
setup_logging(_logging_mode)
|
|
124
|
+
|
|
125
|
+
# Discover and load applications on every app creation (including reloads)
|
|
126
|
+
discover_and_load_prompts()
|
|
127
|
+
|
|
128
|
+
dotenv.load_dotenv(os.getcwd() + "/.env")
|
|
129
|
+
storage_directory = os.getenv("PIXIE_PROMPT_STORAGE_DIR", ".pixie/prompts")
|
|
130
|
+
initialize_prompt_storage(storage_directory)
|
|
131
|
+
|
|
132
|
+
app = FastAPI(
|
|
133
|
+
title="Pixie SDK Server",
|
|
134
|
+
description="Server for running AI applications and agents",
|
|
135
|
+
version="0.1.0",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Add CORS middleware
|
|
139
|
+
app.add_middleware(
|
|
140
|
+
CORSMiddleware,
|
|
141
|
+
allow_origins=["*"], # Allows all origins
|
|
142
|
+
allow_credentials=True,
|
|
143
|
+
allow_methods=["*"], # Allows all methods
|
|
144
|
+
allow_headers=["*"], # Allows all headers
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
# Add GraphQL router with GraphiQL enabled
|
|
148
|
+
graphql_app = GraphQLRouter(
|
|
149
|
+
schema,
|
|
150
|
+
graphiql=True,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
app.include_router(graphql_app, prefix="/graphql")
|
|
154
|
+
|
|
155
|
+
@app.get("/")
|
|
156
|
+
async def root():
|
|
157
|
+
return {
|
|
158
|
+
"message": "Pixie SDK Server",
|
|
159
|
+
"graphiql": "/graphql",
|
|
160
|
+
"version": "0.1.0",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return app
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def start_server(
|
|
167
|
+
host: str = "0.0.0.0",
|
|
168
|
+
port: int = 8000,
|
|
169
|
+
reload: bool = False,
|
|
170
|
+
log_mode: str = "default",
|
|
171
|
+
) -> None:
|
|
172
|
+
"""Start the SDK server.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
host: Host to bind to
|
|
176
|
+
port: Port to bind to
|
|
177
|
+
reload: Enable auto-reload for development
|
|
178
|
+
log_mode: Logging mode - "default", "verbose", or "debug"
|
|
179
|
+
storage_directory: Directory to store prompt definitions
|
|
180
|
+
"""
|
|
181
|
+
global _logging_mode
|
|
182
|
+
_logging_mode = log_mode
|
|
183
|
+
|
|
184
|
+
# Setup logging (will be called again in create_app for reload scenarios)
|
|
185
|
+
setup_logging(log_mode)
|
|
186
|
+
|
|
187
|
+
# Determine server URL
|
|
188
|
+
server_url = f"http://{host}:{port}"
|
|
189
|
+
if host == "0.0.0.0":
|
|
190
|
+
server_url = f"http://127.0.0.1:{port}"
|
|
191
|
+
|
|
192
|
+
# Log server start info
|
|
193
|
+
logger.info("Starting Pixie SDK Server")
|
|
194
|
+
logger.info("Server: %s", server_url)
|
|
195
|
+
logger.info("GraphQL: %s/graphql", server_url)
|
|
196
|
+
|
|
197
|
+
# Display gopixie.ai web link
|
|
198
|
+
encoded_url = quote(f"{server_url}/graphql", safe="")
|
|
199
|
+
pixie_web_url = f"https://gopixie.ai?url={encoded_url}"
|
|
200
|
+
logger.info("")
|
|
201
|
+
logger.info("=" * 60)
|
|
202
|
+
logger.info("")
|
|
203
|
+
logger.info("🎨 Open Pixie Web UI:")
|
|
204
|
+
logger.info("")
|
|
205
|
+
logger.info(" %s", pixie_web_url)
|
|
206
|
+
logger.info("")
|
|
207
|
+
logger.info("=" * 60)
|
|
208
|
+
logger.info("")
|
|
209
|
+
|
|
210
|
+
uvicorn.run(
|
|
211
|
+
"pixie.prompts.server:create_app",
|
|
212
|
+
host=host,
|
|
213
|
+
port=port,
|
|
214
|
+
loop="asyncio",
|
|
215
|
+
reload=reload,
|
|
216
|
+
factory=True,
|
|
217
|
+
log_config=None,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def main():
|
|
222
|
+
"""Start the Pixie server.
|
|
223
|
+
|
|
224
|
+
Loads environment variables and starts the server with auto-reload enabled.
|
|
225
|
+
Supports --verbose and --debug flags for enhanced logging.
|
|
226
|
+
"""
|
|
227
|
+
parser = argparse.ArgumentParser(description="Pixie Prompts development server")
|
|
228
|
+
parser.add_argument(
|
|
229
|
+
"--verbose",
|
|
230
|
+
"-v",
|
|
231
|
+
action="store_true",
|
|
232
|
+
help="Enable verbose logging (INFO+ for all modules)",
|
|
233
|
+
)
|
|
234
|
+
parser.add_argument(
|
|
235
|
+
"--debug",
|
|
236
|
+
"-d",
|
|
237
|
+
action="store_true",
|
|
238
|
+
help="Enable debug logging (DEBUG+ for all modules)",
|
|
239
|
+
)
|
|
240
|
+
parser.add_argument(
|
|
241
|
+
"--port",
|
|
242
|
+
"-p",
|
|
243
|
+
type=int,
|
|
244
|
+
default=None,
|
|
245
|
+
help="Port to run the server on (overrides PIXIE_SDK_PORT env var)",
|
|
246
|
+
)
|
|
247
|
+
args = parser.parse_args()
|
|
248
|
+
|
|
249
|
+
# Determine logging mode
|
|
250
|
+
log_mode = "default"
|
|
251
|
+
if args.debug:
|
|
252
|
+
log_mode = "debug"
|
|
253
|
+
elif args.verbose:
|
|
254
|
+
log_mode = "verbose"
|
|
255
|
+
|
|
256
|
+
dotenv.load_dotenv(os.getcwd() + "/.env")
|
|
257
|
+
port = args.port or int(os.getenv("PIXIE_SDK_PORT", "8000"))
|
|
258
|
+
|
|
259
|
+
start_server(port=port, reload=True, log_mode=log_mode)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
main()
|
pixie/prompts/storage.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from types import NoneType
|
|
5
|
+
from typing import Any, Dict, Protocol, Self, TypedDict
|
|
6
|
+
|
|
7
|
+
from jsonsubschema import isSubschema
|
|
8
|
+
|
|
9
|
+
from .prompt import (
|
|
10
|
+
BasePrompt,
|
|
11
|
+
BaseUntypedPrompt,
|
|
12
|
+
Prompt,
|
|
13
|
+
TPromptVar,
|
|
14
|
+
variables_definition_to_schema,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PromptStorage(Protocol):
|
|
22
|
+
|
|
23
|
+
def load(self) -> None: ...
|
|
24
|
+
|
|
25
|
+
def exists(self, prompt_id: str) -> bool: ...
|
|
26
|
+
|
|
27
|
+
def save(self, prompt: BaseUntypedPrompt) -> None: ...
|
|
28
|
+
|
|
29
|
+
def get(self, prompt_id: str) -> BaseUntypedPrompt: ...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class _BasePromptJson(TypedDict):
|
|
33
|
+
versions: Dict[str, str]
|
|
34
|
+
defaultVersionId: str
|
|
35
|
+
variablesSchema: Dict[str, Any]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class _FilePromptStorage(PromptStorage):
|
|
39
|
+
|
|
40
|
+
def __init__(self, directory: str) -> None:
|
|
41
|
+
self._directory = directory
|
|
42
|
+
self._prompts: Dict[str, BaseUntypedPrompt] = {}
|
|
43
|
+
self.load()
|
|
44
|
+
|
|
45
|
+
def load(self) -> None:
|
|
46
|
+
"""prompts that are in storage"""
|
|
47
|
+
if not os.path.exists(self._directory):
|
|
48
|
+
os.makedirs(self._directory)
|
|
49
|
+
for filename in os.listdir(self._directory):
|
|
50
|
+
if filename.endswith(".json"):
|
|
51
|
+
prompt_id = filename[:-5] # remove .json
|
|
52
|
+
filepath = os.path.join(self._directory, filename)
|
|
53
|
+
with open(filepath, "r") as f:
|
|
54
|
+
data: _BasePromptJson = json.load(f)
|
|
55
|
+
versions = data["versions"]
|
|
56
|
+
default_version_id = data["defaultVersionId"]
|
|
57
|
+
variables_schema = data["variablesSchema"]
|
|
58
|
+
prompt = BaseUntypedPrompt(
|
|
59
|
+
id=prompt_id,
|
|
60
|
+
versions=versions,
|
|
61
|
+
default_version_id=default_version_id,
|
|
62
|
+
variables_schema=variables_schema,
|
|
63
|
+
)
|
|
64
|
+
self._prompts[prompt_id] = prompt
|
|
65
|
+
|
|
66
|
+
def exists(self, prompt_id: str) -> bool:
|
|
67
|
+
return prompt_id in self._prompts
|
|
68
|
+
|
|
69
|
+
def save(self, prompt: BaseUntypedPrompt) -> bool:
|
|
70
|
+
prompt_id = prompt.id
|
|
71
|
+
original = self._prompts.get(prompt_id)
|
|
72
|
+
new_schema = prompt.get_variables_schema()
|
|
73
|
+
if original:
|
|
74
|
+
original_schema = original.get_variables_schema()
|
|
75
|
+
if not isSubschema(original_schema, new_schema):
|
|
76
|
+
raise TypeError(
|
|
77
|
+
"Original schema must be a subschema of the new schema."
|
|
78
|
+
)
|
|
79
|
+
data: _BasePromptJson = {
|
|
80
|
+
"versions": prompt.get_versions(),
|
|
81
|
+
"defaultVersionId": prompt.get_default_version_id(),
|
|
82
|
+
"variablesSchema": prompt.get_variables_schema(),
|
|
83
|
+
}
|
|
84
|
+
filepath = os.path.join(self._directory, f"{prompt_id}.json")
|
|
85
|
+
with open(filepath, "w") as f:
|
|
86
|
+
json.dump(data, f, indent=2)
|
|
87
|
+
try:
|
|
88
|
+
BasePrompt.update_prompt_registry(prompt)
|
|
89
|
+
except KeyError:
|
|
90
|
+
# Prompt not in type prompt registry yet, meaning there's no usage in code
|
|
91
|
+
# thus this untyped prompt would just be stored but not used in code
|
|
92
|
+
pass
|
|
93
|
+
self._prompts[prompt_id] = prompt
|
|
94
|
+
return original is None
|
|
95
|
+
|
|
96
|
+
def get(self, prompt_id: str) -> BaseUntypedPrompt:
|
|
97
|
+
return self._prompts[prompt_id]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
_storage_instance: PromptStorage | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# TODO allow other storage types later
|
|
104
|
+
def initialize_prompt_storage(directory: str) -> None:
|
|
105
|
+
global _storage_instance
|
|
106
|
+
if _storage_instance is not None:
|
|
107
|
+
raise RuntimeError("Prompt storage has already been initialized.")
|
|
108
|
+
_storage_instance = _FilePromptStorage(directory)
|
|
109
|
+
logger.info(f"Initialized prompt storage at directory: {directory}")
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class StorageBackedPrompt(Prompt[TPromptVar]):
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
id: str,
|
|
117
|
+
*,
|
|
118
|
+
variables_definition: type[TPromptVar] = NoneType,
|
|
119
|
+
) -> None:
|
|
120
|
+
self._id = id
|
|
121
|
+
self._variables_definition = variables_definition
|
|
122
|
+
self._prompt: BasePrompt[TPromptVar] | None = None
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def id(self) -> str:
|
|
126
|
+
return self._id
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def variables_definition(self) -> type[TPromptVar]:
|
|
130
|
+
return self._variables_definition
|
|
131
|
+
|
|
132
|
+
def get_variables_schema(self) -> dict[str, Any]:
|
|
133
|
+
return variables_definition_to_schema(self._variables_definition)
|
|
134
|
+
|
|
135
|
+
def _get_prompt(self) -> BasePrompt[TPromptVar]:
|
|
136
|
+
if _storage_instance is None:
|
|
137
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
138
|
+
if self._prompt is None:
|
|
139
|
+
untyped_prompt = _storage_instance.get(self.id)
|
|
140
|
+
self._prompt = BasePrompt.from_untyped(
|
|
141
|
+
untyped_prompt,
|
|
142
|
+
variables_definition=self.variables_definition,
|
|
143
|
+
)
|
|
144
|
+
schema_from_storage = untyped_prompt.get_variables_schema()
|
|
145
|
+
schema_from_definition = self.get_variables_schema()
|
|
146
|
+
if not isSubschema(schema_from_definition, schema_from_storage):
|
|
147
|
+
raise TypeError(
|
|
148
|
+
"Schema from definition is not a subschema of the schema from storage."
|
|
149
|
+
)
|
|
150
|
+
return self._prompt
|
|
151
|
+
|
|
152
|
+
def actualize(self) -> Self:
|
|
153
|
+
self._get_prompt()
|
|
154
|
+
return self
|
|
155
|
+
|
|
156
|
+
def exists_in_storage(self) -> bool:
|
|
157
|
+
if _storage_instance is None:
|
|
158
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
159
|
+
try:
|
|
160
|
+
self.actualize()
|
|
161
|
+
return True
|
|
162
|
+
except KeyError:
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
def get_versions(self) -> dict[str, str]:
|
|
166
|
+
prompt = self._get_prompt()
|
|
167
|
+
return prompt.get_versions()
|
|
168
|
+
|
|
169
|
+
def get_version_count(self) -> int:
|
|
170
|
+
try:
|
|
171
|
+
prompt = self._get_prompt()
|
|
172
|
+
versions_dict = prompt.get_versions()
|
|
173
|
+
return len(versions_dict)
|
|
174
|
+
except KeyError:
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
def get_default_version_id(self) -> str:
|
|
178
|
+
prompt = self._get_prompt()
|
|
179
|
+
return prompt.get_default_version_id()
|
|
180
|
+
|
|
181
|
+
def compile(
|
|
182
|
+
self,
|
|
183
|
+
variables: TPromptVar = None,
|
|
184
|
+
*,
|
|
185
|
+
version_id: str | None = None,
|
|
186
|
+
) -> str:
|
|
187
|
+
prompt = self._get_prompt()
|
|
188
|
+
return prompt.compile(variables=variables, version_id=version_id)
|
|
189
|
+
|
|
190
|
+
def append_version(
|
|
191
|
+
self,
|
|
192
|
+
version_id: str,
|
|
193
|
+
content: str,
|
|
194
|
+
set_as_default: bool = False,
|
|
195
|
+
) -> BasePrompt[TPromptVar]:
|
|
196
|
+
if _storage_instance is None:
|
|
197
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
198
|
+
if self.exists_in_storage():
|
|
199
|
+
prompt = self._get_prompt()
|
|
200
|
+
prompt.append_version(
|
|
201
|
+
version_id=version_id,
|
|
202
|
+
content=content,
|
|
203
|
+
set_as_default=set_as_default,
|
|
204
|
+
)
|
|
205
|
+
_storage_instance.save(prompt)
|
|
206
|
+
return prompt
|
|
207
|
+
else:
|
|
208
|
+
# it should be safe to assume there's no actualized prompt for this id
|
|
209
|
+
# thus it should be same to create a new instance of BasePrompt
|
|
210
|
+
new_prompt = BasePrompt(
|
|
211
|
+
id=self.id,
|
|
212
|
+
versions={version_id: content},
|
|
213
|
+
variables_definition=self.variables_definition,
|
|
214
|
+
default_version_id=version_id,
|
|
215
|
+
)
|
|
216
|
+
_storage_instance.save(new_prompt)
|
|
217
|
+
return new_prompt
|
|
218
|
+
|
|
219
|
+
def update_default_version_id(
|
|
220
|
+
self,
|
|
221
|
+
version_id: str,
|
|
222
|
+
) -> BasePrompt[TPromptVar]:
|
|
223
|
+
if _storage_instance is None:
|
|
224
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
225
|
+
prompt = self._get_prompt()
|
|
226
|
+
prompt.update_default_version_id(version_id)
|
|
227
|
+
_storage_instance.save(prompt)
|
|
228
|
+
return prompt
|
pixie/tests/__init__.py
ADDED
|
File without changes
|