pixie-prompts 0.1.1__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 +19 -0
- pixie/prompts/file_watcher.py +327 -0
- pixie/prompts/graphql.py +212 -0
- pixie/prompts/prompt.py +373 -0
- pixie/prompts/prompt_management.py +82 -0
- pixie/prompts/server.py +231 -0
- pixie/prompts/storage.py +399 -0
- pixie_prompts-0.1.1.dist-info/METADATA +36 -0
- pixie_prompts-0.1.1.dist-info/RECORD +12 -0
- pixie_prompts-0.1.1.dist-info/WHEEL +4 -0
- pixie_prompts-0.1.1.dist-info/entry_points.txt +3 -0
- pixie_prompts-0.1.1.dist-info/licenses/LICENSE +21 -0
pixie/prompts/storage.py
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from types import NoneType
|
|
6
|
+
from typing import Any, Dict, NotRequired, Protocol, Self, TypedDict
|
|
7
|
+
|
|
8
|
+
from jsonsubschema import isSubschema
|
|
9
|
+
|
|
10
|
+
from .prompt import (
|
|
11
|
+
BasePrompt,
|
|
12
|
+
BaseUntypedPrompt,
|
|
13
|
+
Prompt,
|
|
14
|
+
TPromptVar,
|
|
15
|
+
variables_definition_to_schema,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
VERSION_FILE_EXTENSION = ".jinja"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class _PromptLoadFailure:
|
|
27
|
+
prompt_id: str | None
|
|
28
|
+
path: str
|
|
29
|
+
error: Exception
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PromptLoadError(Exception):
|
|
33
|
+
|
|
34
|
+
def __init__(self, failures: list[_PromptLoadFailure]):
|
|
35
|
+
self.failures = failures
|
|
36
|
+
message_lines = [
|
|
37
|
+
f"- {failure.prompt_id or '<unknown>'} ({failure.path}): {failure.error}"
|
|
38
|
+
for failure in failures
|
|
39
|
+
]
|
|
40
|
+
message = "Failed to load prompts:\n" + "\n".join(message_lines)
|
|
41
|
+
super().__init__(message)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BaseUntypedPromptWithCreationTime(BaseUntypedPrompt):
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
id: str,
|
|
50
|
+
versions: dict[str, str],
|
|
51
|
+
default_version_id: str,
|
|
52
|
+
variables_schema: dict[str, Any] | None = None,
|
|
53
|
+
version_creation_times: dict[str, float],
|
|
54
|
+
) -> None:
|
|
55
|
+
super().__init__(
|
|
56
|
+
id=id,
|
|
57
|
+
versions=versions,
|
|
58
|
+
default_version_id=default_version_id,
|
|
59
|
+
variables_schema=variables_schema,
|
|
60
|
+
)
|
|
61
|
+
self._version_creation_times = version_creation_times
|
|
62
|
+
|
|
63
|
+
def get_version_creation_time(self, version_id: str) -> float:
|
|
64
|
+
return self._version_creation_times[version_id]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PromptStorage(Protocol):
|
|
68
|
+
|
|
69
|
+
def load(self, *, raise_on_error: bool = True) -> list[_PromptLoadFailure]: ...
|
|
70
|
+
|
|
71
|
+
def exists(self, prompt_id: str) -> bool: ...
|
|
72
|
+
|
|
73
|
+
def save(self, prompt: BaseUntypedPrompt) -> bool: ...
|
|
74
|
+
|
|
75
|
+
def get(self, prompt_id: str) -> BaseUntypedPromptWithCreationTime: ...
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class _BasePromptMetadata(TypedDict):
|
|
79
|
+
defaultVersionId: str
|
|
80
|
+
variablesSchema: NotRequired[Dict[str, Any]]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class _FilePromptStorage(PromptStorage):
|
|
84
|
+
|
|
85
|
+
def __init__(self, directory: str, *, raise_on_error: bool = True) -> None:
|
|
86
|
+
self._directory = directory
|
|
87
|
+
self._prompts: Dict[str, BaseUntypedPromptWithCreationTime] = {}
|
|
88
|
+
self._load_failures: list[_PromptLoadFailure] = []
|
|
89
|
+
self.load(raise_on_error=raise_on_error)
|
|
90
|
+
|
|
91
|
+
def load(self, *, raise_on_error: bool = True) -> list[_PromptLoadFailure]:
|
|
92
|
+
"""Load prompts from storage with error isolation.
|
|
93
|
+
|
|
94
|
+
Continues loading valid prompts even if some fail and aggregates failures.
|
|
95
|
+
"""
|
|
96
|
+
logger.info("Loading prompts from directory %s", self._directory)
|
|
97
|
+
self._prompts.clear()
|
|
98
|
+
self._load_failures = []
|
|
99
|
+
if not os.path.exists(self._directory):
|
|
100
|
+
os.makedirs(self._directory)
|
|
101
|
+
for entry in os.listdir(self._directory):
|
|
102
|
+
prompt_path = os.path.join(self._directory, entry)
|
|
103
|
+
if not os.path.isdir(prompt_path):
|
|
104
|
+
logger.debug("Skipping non-directory entry at %s", prompt_path)
|
|
105
|
+
continue
|
|
106
|
+
try:
|
|
107
|
+
metadata_path = os.path.join(prompt_path, "metadata.json")
|
|
108
|
+
metadata: _BasePromptMetadata | None = None
|
|
109
|
+
if os.path.isfile(metadata_path):
|
|
110
|
+
with open(metadata_path, "r") as f:
|
|
111
|
+
metadata = json.load(f)
|
|
112
|
+
|
|
113
|
+
versions: dict[str, str] = {}
|
|
114
|
+
version_creation_times: dict[str, float] = {}
|
|
115
|
+
for filename in os.listdir(prompt_path):
|
|
116
|
+
if not filename.endswith(VERSION_FILE_EXTENSION):
|
|
117
|
+
continue
|
|
118
|
+
version_id, _ = os.path.splitext(filename)
|
|
119
|
+
version_path = os.path.join(prompt_path, filename)
|
|
120
|
+
with open(version_path, "r") as vf:
|
|
121
|
+
versions[version_id] = vf.read()
|
|
122
|
+
version_creation_times[version_id] = os.path.getctime(version_path)
|
|
123
|
+
|
|
124
|
+
if not versions:
|
|
125
|
+
raise KeyError("No versions provided for the prompt.")
|
|
126
|
+
|
|
127
|
+
if metadata is not None:
|
|
128
|
+
default_version_id = metadata["defaultVersionId"]
|
|
129
|
+
variables_schema = metadata.get("variablesSchema", None)
|
|
130
|
+
else:
|
|
131
|
+
default_version_id, _ = max(
|
|
132
|
+
version_creation_times.items(),
|
|
133
|
+
key=lambda item: (item[1], item[0]),
|
|
134
|
+
)
|
|
135
|
+
variables_schema = None
|
|
136
|
+
|
|
137
|
+
if default_version_id not in versions:
|
|
138
|
+
raise KeyError(
|
|
139
|
+
f"Default version '{default_version_id}' not found for prompt '{entry}'."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
prompt = BaseUntypedPromptWithCreationTime(
|
|
143
|
+
id=entry,
|
|
144
|
+
versions=versions,
|
|
145
|
+
default_version_id=default_version_id,
|
|
146
|
+
variables_schema=variables_schema,
|
|
147
|
+
version_creation_times=version_creation_times,
|
|
148
|
+
)
|
|
149
|
+
self._prompts[entry] = prompt
|
|
150
|
+
logger.debug(
|
|
151
|
+
"Loaded prompt '%s' with %d version(s)", entry, len(versions)
|
|
152
|
+
)
|
|
153
|
+
except Exception as exc: # noqa: BLE001
|
|
154
|
+
logger.exception("Failed to load prompt '%s' at %s", entry, prompt_path)
|
|
155
|
+
self._load_failures.append(
|
|
156
|
+
_PromptLoadFailure(prompt_id=entry, path=prompt_path, error=exc)
|
|
157
|
+
)
|
|
158
|
+
if self._load_failures:
|
|
159
|
+
logger.warning(
|
|
160
|
+
"Completed loading prompts with %d failure(s)", len(self._load_failures)
|
|
161
|
+
)
|
|
162
|
+
if raise_on_error:
|
|
163
|
+
raise PromptLoadError(self._load_failures)
|
|
164
|
+
else:
|
|
165
|
+
logger.info("Loaded %d prompt(s) successfully", len(self._prompts))
|
|
166
|
+
return list(self._load_failures)
|
|
167
|
+
|
|
168
|
+
@property
|
|
169
|
+
def load_failures(self) -> list[_PromptLoadFailure]:
|
|
170
|
+
return list(self._load_failures)
|
|
171
|
+
|
|
172
|
+
def exists(self, prompt_id: str) -> bool:
|
|
173
|
+
return prompt_id in self._prompts
|
|
174
|
+
|
|
175
|
+
def save(self, prompt: BaseUntypedPrompt) -> bool:
|
|
176
|
+
prompt_id = prompt.id
|
|
177
|
+
original = self._prompts.get(prompt_id)
|
|
178
|
+
new_schema = prompt.get_variables_schema()
|
|
179
|
+
if original:
|
|
180
|
+
original_schema = original.get_variables_schema()
|
|
181
|
+
if not isSubschema(original_schema, new_schema):
|
|
182
|
+
raise TypeError(
|
|
183
|
+
"Original schema must be a subschema of the new schema."
|
|
184
|
+
)
|
|
185
|
+
prompt_dir = os.path.join(self._directory, prompt_id)
|
|
186
|
+
os.makedirs(prompt_dir, exist_ok=True)
|
|
187
|
+
|
|
188
|
+
versions = prompt.get_versions()
|
|
189
|
+
version_ids = set(versions.keys())
|
|
190
|
+
existing_versions = {
|
|
191
|
+
os.path.splitext(filename)[0]
|
|
192
|
+
for filename in os.listdir(prompt_dir)
|
|
193
|
+
if filename.endswith(VERSION_FILE_EXTENSION)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# Validate that we are not overwriting existing content with new data
|
|
197
|
+
for version_id in version_ids & existing_versions:
|
|
198
|
+
version_path = os.path.join(
|
|
199
|
+
prompt_dir, f"{version_id}{VERSION_FILE_EXTENSION}"
|
|
200
|
+
)
|
|
201
|
+
with open(version_path, "r") as vf:
|
|
202
|
+
existing_content = vf.read()
|
|
203
|
+
if existing_content != versions[version_id]:
|
|
204
|
+
raise ValueError(
|
|
205
|
+
f"Version '{version_id}' already exists with different content."
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
metadata: _BasePromptMetadata = {
|
|
209
|
+
"defaultVersionId": prompt.get_default_version_id(),
|
|
210
|
+
"variablesSchema": prompt.get_variables_schema(),
|
|
211
|
+
}
|
|
212
|
+
metadata_path = os.path.join(prompt_dir, "metadata.json")
|
|
213
|
+
with open(metadata_path, "w") as f:
|
|
214
|
+
json.dump(metadata, f, indent=2)
|
|
215
|
+
|
|
216
|
+
# Write only new versions; existing identical versions are left untouched
|
|
217
|
+
for version_id, content in versions.items():
|
|
218
|
+
version_path = os.path.join(
|
|
219
|
+
prompt_dir, f"{version_id}{VERSION_FILE_EXTENSION}"
|
|
220
|
+
)
|
|
221
|
+
if os.path.exists(version_path):
|
|
222
|
+
continue
|
|
223
|
+
with open(version_path, "w") as vf:
|
|
224
|
+
vf.write(content)
|
|
225
|
+
|
|
226
|
+
for stale_version in existing_versions - set(versions.keys()):
|
|
227
|
+
stale_path = os.path.join(
|
|
228
|
+
prompt_dir, f"{stale_version}{VERSION_FILE_EXTENSION}"
|
|
229
|
+
)
|
|
230
|
+
os.remove(stale_path)
|
|
231
|
+
|
|
232
|
+
version_creation_times = {
|
|
233
|
+
version_id: os.path.getctime(
|
|
234
|
+
os.path.join(prompt_dir, f"{version_id}{VERSION_FILE_EXTENSION}")
|
|
235
|
+
)
|
|
236
|
+
for version_id in versions.keys()
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
stored_prompt = BaseUntypedPromptWithCreationTime(
|
|
240
|
+
id=prompt_id,
|
|
241
|
+
versions=versions,
|
|
242
|
+
default_version_id=prompt.get_default_version_id(),
|
|
243
|
+
variables_schema=prompt.get_variables_schema(),
|
|
244
|
+
version_creation_times=version_creation_times,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
BasePrompt.update_prompt_registry(stored_prompt)
|
|
249
|
+
except KeyError:
|
|
250
|
+
# Prompt not in type prompt registry yet, meaning there's no usage in code
|
|
251
|
+
# thus this untyped prompt would just be stored but not used in code
|
|
252
|
+
pass
|
|
253
|
+
self._prompts[prompt_id] = stored_prompt
|
|
254
|
+
return original is None
|
|
255
|
+
|
|
256
|
+
def get(self, prompt_id: str) -> BaseUntypedPromptWithCreationTime:
|
|
257
|
+
return self._prompts[prompt_id]
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
_storage_instance: PromptStorage | None = None
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# TODO allow other storage types later
|
|
264
|
+
def initialize_prompt_storage(directory: str) -> None:
|
|
265
|
+
global _storage_instance
|
|
266
|
+
if _storage_instance is not None:
|
|
267
|
+
raise RuntimeError("Prompt storage has already been initialized.")
|
|
268
|
+
storage = _FilePromptStorage(directory, raise_on_error=False)
|
|
269
|
+
_storage_instance = storage
|
|
270
|
+
logger.info("Initialized prompt storage at directory: %s", directory)
|
|
271
|
+
if storage.load_failures:
|
|
272
|
+
raise PromptLoadError(storage.load_failures)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class StorageBackedPrompt(Prompt[TPromptVar]):
|
|
276
|
+
|
|
277
|
+
def __init__(
|
|
278
|
+
self,
|
|
279
|
+
id: str,
|
|
280
|
+
*,
|
|
281
|
+
variables_definition: type[TPromptVar] = NoneType,
|
|
282
|
+
) -> None:
|
|
283
|
+
self._id = id
|
|
284
|
+
self._variables_definition = variables_definition
|
|
285
|
+
self._prompt: BasePrompt[TPromptVar] | None = None
|
|
286
|
+
|
|
287
|
+
@property
|
|
288
|
+
def id(self) -> str:
|
|
289
|
+
return self._id
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def variables_definition(self) -> type[TPromptVar]:
|
|
293
|
+
return self._variables_definition
|
|
294
|
+
|
|
295
|
+
def get_variables_schema(self) -> dict[str, Any]:
|
|
296
|
+
return variables_definition_to_schema(self._variables_definition)
|
|
297
|
+
|
|
298
|
+
def _get_prompt(self) -> BasePrompt[TPromptVar]:
|
|
299
|
+
if _storage_instance is None:
|
|
300
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
301
|
+
if self._prompt is None:
|
|
302
|
+
untyped_prompt = _storage_instance.get(self.id)
|
|
303
|
+
self._prompt = BasePrompt.from_untyped(
|
|
304
|
+
untyped_prompt,
|
|
305
|
+
variables_definition=self.variables_definition,
|
|
306
|
+
)
|
|
307
|
+
schema_from_storage = untyped_prompt.get_variables_schema()
|
|
308
|
+
schema_from_definition = self.get_variables_schema()
|
|
309
|
+
if not isSubschema(schema_from_definition, schema_from_storage):
|
|
310
|
+
raise TypeError(
|
|
311
|
+
"Schema from definition is not a subschema of the schema from storage."
|
|
312
|
+
)
|
|
313
|
+
return self._prompt
|
|
314
|
+
|
|
315
|
+
def actualize(self) -> Self:
|
|
316
|
+
self._get_prompt()
|
|
317
|
+
return self
|
|
318
|
+
|
|
319
|
+
def exists_in_storage(self) -> bool:
|
|
320
|
+
if _storage_instance is None:
|
|
321
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
322
|
+
try:
|
|
323
|
+
self.actualize()
|
|
324
|
+
return True
|
|
325
|
+
except KeyError:
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
def get_versions(self) -> dict[str, str]:
|
|
329
|
+
prompt = self._get_prompt()
|
|
330
|
+
return prompt.get_versions()
|
|
331
|
+
|
|
332
|
+
def get_version_creation_time(self, version_id: str) -> float:
|
|
333
|
+
if _storage_instance is None:
|
|
334
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
335
|
+
prompt_with_ctime = _storage_instance.get(self.id)
|
|
336
|
+
if not prompt_with_ctime:
|
|
337
|
+
raise KeyError(f"Prompt with id '{self.id}' not found in storage.")
|
|
338
|
+
return prompt_with_ctime.get_version_creation_time(version_id)
|
|
339
|
+
|
|
340
|
+
def get_version_count(self) -> int:
|
|
341
|
+
try:
|
|
342
|
+
prompt = self._get_prompt()
|
|
343
|
+
versions_dict = prompt.get_versions()
|
|
344
|
+
return len(versions_dict)
|
|
345
|
+
except KeyError:
|
|
346
|
+
return 0
|
|
347
|
+
|
|
348
|
+
def get_default_version_id(self) -> str:
|
|
349
|
+
prompt = self._get_prompt()
|
|
350
|
+
return prompt.get_default_version_id()
|
|
351
|
+
|
|
352
|
+
def compile(
|
|
353
|
+
self,
|
|
354
|
+
variables: TPromptVar = None,
|
|
355
|
+
*,
|
|
356
|
+
version_id: str | None = None,
|
|
357
|
+
) -> str:
|
|
358
|
+
prompt = self._get_prompt()
|
|
359
|
+
return prompt.compile(variables=variables, version_id=version_id)
|
|
360
|
+
|
|
361
|
+
def append_version(
|
|
362
|
+
self,
|
|
363
|
+
version_id: str,
|
|
364
|
+
content: str,
|
|
365
|
+
set_as_default: bool = False,
|
|
366
|
+
) -> BasePrompt[TPromptVar]:
|
|
367
|
+
if _storage_instance is None:
|
|
368
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
369
|
+
if self.exists_in_storage():
|
|
370
|
+
prompt = self._get_prompt()
|
|
371
|
+
prompt.append_version(
|
|
372
|
+
version_id=version_id,
|
|
373
|
+
content=content,
|
|
374
|
+
set_as_default=set_as_default,
|
|
375
|
+
)
|
|
376
|
+
_storage_instance.save(prompt)
|
|
377
|
+
return prompt
|
|
378
|
+
else:
|
|
379
|
+
# it should be safe to assume there's no actualized prompt for this id
|
|
380
|
+
# thus it should be same to create a new instance of BasePrompt
|
|
381
|
+
new_prompt = BasePrompt(
|
|
382
|
+
id=self.id,
|
|
383
|
+
versions={version_id: content},
|
|
384
|
+
variables_definition=self.variables_definition,
|
|
385
|
+
default_version_id=version_id,
|
|
386
|
+
)
|
|
387
|
+
_storage_instance.save(new_prompt)
|
|
388
|
+
return new_prompt
|
|
389
|
+
|
|
390
|
+
def update_default_version_id(
|
|
391
|
+
self,
|
|
392
|
+
version_id: str,
|
|
393
|
+
) -> BasePrompt[TPromptVar]:
|
|
394
|
+
if _storage_instance is None:
|
|
395
|
+
raise RuntimeError("Prompt storage has not been initialized.")
|
|
396
|
+
prompt = self._get_prompt()
|
|
397
|
+
prompt.update_default_version_id(version_id)
|
|
398
|
+
_storage_instance.save(prompt)
|
|
399
|
+
return prompt
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pixie-prompts
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Code-first, type-safe prompt management
|
|
5
|
+
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: Yiou Li
|
|
8
|
+
Author-email: yol@gopixie.ai
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Provides-Extra: server
|
|
18
|
+
Requires-Dist: colorlog (>=6.10.1) ; extra == "server"
|
|
19
|
+
Requires-Dist: dotenv (>=0.9.9) ; extra == "server"
|
|
20
|
+
Requires-Dist: fastapi (>=0.128.0) ; extra == "server"
|
|
21
|
+
Requires-Dist: jinja2 (>=3.1.6,<4.0.0)
|
|
22
|
+
Requires-Dist: jsonsubschema (>=0.0.7,<0.0.8)
|
|
23
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
24
|
+
Requires-Dist: strawberry-graphql (>=0.288.1) ; extra == "server"
|
|
25
|
+
Requires-Dist: uvicorn (>=0.40.0) ; extra == "server"
|
|
26
|
+
Requires-Dist: watchdog (>=6.0.0) ; extra == "server"
|
|
27
|
+
Project-URL: Changelog, https://github.com/yiouli/pixie-prompts/commits/main/
|
|
28
|
+
Project-URL: Documentation, https://yiouli.github.io/pixie-prompts/
|
|
29
|
+
Project-URL: Homepage, https://gopixie.ai
|
|
30
|
+
Project-URL: Issues, https://github.com/yiouli/pixie-prompts/issues
|
|
31
|
+
Project-URL: Repository, https://github.com/yiouli/pixie-prompts
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# pixie-prompts
|
|
35
|
+
Code-first, type-checked prompt management.
|
|
36
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
pixie/prompts/__init__.py,sha256=ZueU9cJ7aiVHBQYH4g3MXAFtjQwTfvvpy3d8ZTtBQ2c,396
|
|
2
|
+
pixie/prompts/file_watcher.py,sha256=F-p84r820en6qb3vNjJleWyUp4AR2UC7ZCGvWNJq0sM,10530
|
|
3
|
+
pixie/prompts/graphql.py,sha256=5pouvFlKdZxJYxxd2cRvxHBCmvpc5JEAReflPYfzqko,6548
|
|
4
|
+
pixie/prompts/prompt.py,sha256=7nBn1PCXNOVL6OflHak7MG9rlZ4Ooa14eTamYk2mE3I,11472
|
|
5
|
+
pixie/prompts/prompt_management.py,sha256=gq5Eklqy2_Sq8jATVae4eANNmyFE8s8a9cedxWs2P_Y,2816
|
|
6
|
+
pixie/prompts/server.py,sha256=_BsPfE_VJTvqNOaJPf14LXT-ubYRWbNi1NPFvAgXi5s,6433
|
|
7
|
+
pixie/prompts/storage.py,sha256=syVHO5IWZXtN20ozPoBq_Anbu0NAH056EWbvlNNWLGU,14448
|
|
8
|
+
pixie_prompts-0.1.1.dist-info/METADATA,sha256=2rG-8DTU6Y9aXGYM3PwrK74wwG1Y9rCaBT0HsCSN9MA,1478
|
|
9
|
+
pixie_prompts-0.1.1.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
|
|
10
|
+
pixie_prompts-0.1.1.dist-info/entry_points.txt,sha256=SWOSFuUXDxkJMmf28u7E0Go_LcEpofz7NAlV70Cp8Es,48
|
|
11
|
+
pixie_prompts-0.1.1.dist-info/licenses/LICENSE,sha256=nZoehBpdSXe6iTF2ZWzM-fgXdXECUZ0J8LrW_1tBwyk,1064
|
|
12
|
+
pixie_prompts-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yiou Li
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|