Skip to content

API Docs

PromptStore

promptstore.store.PromptStore

Source code in src/promptstore/store.py
class PromptStore:
    def __init__(self, location: Union[str, Path], readonly: bool = False):
        """Initialize a new PromptStore.

        Args:
            location: Path to the directory where prompts will be stored
            readonly: If True, the store will be read-only
        """
        self.location = Path(location)
        self.readonly = readonly
        self.index_path = self.location / "index.json"
        self._init_storage()

    def _init_storage(self):
        """Initialize the storage directory and index."""
        self.location.mkdir(parents=True, exist_ok=True)

        if not self.index_path.exists() and not self.readonly:
            self._save_index({})

    def _load_index(self) -> Dict:
        """Load the prompt index."""
        if not self.index_path.exists():
            return {}
        with open(self.index_path, "r", encoding="utf-8") as f:
            return json.load(f)

    def _save_index(self, index: Dict):
        """Save the prompt index."""
        if self.readonly:
            raise ReadOnlyStoreError("Cannot modify a read-only prompt store")
        with open(self.index_path, "w", encoding="utf-8") as f:
            json.dump(index, f, indent=2, ensure_ascii=False)

    @classmethod
    def from_dict(cls, prompts: Dict, readonly: bool = True) -> "PromptStore":
        """Create a PromptStore from a dictionary.

        Args:
            prompts: Dictionary of prompt data
            readonly: If True, the store will be read-only

        Returns:
            PromptStore: A prompt store initialized with the given prompts
        """
        import tempfile

        temp_dir = Path(tempfile.mkdtemp())
        store = cls(temp_dir, readonly=readonly)

        with open(store.index_path, "w", encoding="utf-8") as f:
            json.dump(prompts, f, indent=2, ensure_ascii=False)

        return store

    @classmethod
    def from_file(cls, path: Union[str, Path], readonly: bool = True) -> "PromptStore":
        """Create a PromptStore from a JSON file.

        Args:
            path: Path to the JSON file containing prompts
            readonly: If True, the store will be read-only

        Returns:
            PromptStore: A prompt store initialized with the prompts from the file
        """
        path = Path(path)
        if not path.exists():
            raise FileNotFoundError(f"Prompt file not found: {path}")

        if path.is_file():
            # If it's a single JSON file
            with open(path, "r", encoding="utf-8") as f:
                prompts = json.load(f)
        else:
            # If it's a directory containing an index.json
            index_path = path / "index.json"
            if not index_path.exists():
                raise FileNotFoundError(f"No index.json found in directory: {path}")
            with open(index_path, "r", encoding="utf-8") as f:
                prompts = json.load(f)

        return cls.from_dict(prompts, readonly=readonly)

    def add(
        self,
        content: str,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
    ) -> Prompt:
        """Add a new prompt to the store.

        Args:
            content: The content of the prompt
            description: A description of the prompt
            tags: A list of tags for the prompt

        Returns:
            Prompt: The newly created prompt
        """
        if self.readonly:
            raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

        prompt_uuid = str(uuid.uuid4())
        now = datetime.utcnow().isoformat()

        prompt_data = {
            "uuid": prompt_uuid,
            "content": content,
            "description": description,
            "version": 1,
            "versions": [
                {
                    "content": content,
                    "description": description,
                    "version": 1,
                    "created_at": now,
                }
            ],
            "tags": tags or [],
            "created_at": now,
            "updated_at": now,
        }

        index = self._load_index()
        index[prompt_uuid] = prompt_data
        self._save_index(index)

        return Prompt(
            uuid=prompt_uuid,
            content=content,
            description=description,
            version=1,
            tags=tags or [],
            timestamp=now,
        )

    def get(self, uuid: str, version: Optional[int] = None) -> Prompt:
        """Retrieve a prompt by its UUID.

        Args:
            uuid: The UUID of the prompt to retrieve
            version: The version of the prompt to retrieve

        Returns:
            Prompt: The prompt object
        """
        index = self._load_index()
        if uuid not in index:
            raise PromptNotFoundError(f"Prompt with UUID {uuid} not found")

        prompt_data = index[uuid]

        if version:
            version_data = next(
                (v for v in prompt_data["versions"] if v["version"] == version), None
            )
            if not version_data:
                raise PromptNotFoundError(
                    f"Version {version} of prompt {uuid} not found"
                )
            content = version_data["content"]
            description = version_data["description"]
            timestamp = version_data["created_at"]
        else:
            content = prompt_data["content"]
            description = prompt_data["description"]
            timestamp = prompt_data["updated_at"]

        return Prompt(
            uuid=uuid,
            content=content,
            description=description,
            version=prompt_data["version"],
            tags=prompt_data["tags"],
            timestamp=timestamp,
        )

    def find(self, query: str, field: str = "description") -> List[Prompt]:
        """Search for prompts based on a query.

        Args:
            query: The search query
            field: The field to search in (description, content, or tags)

        Returns:
            List[Prompt]: A list of prompts matching the query
        """
        if field not in ("description", "content", "tags"):
            raise ValueError("Field must be 'description', 'content', or 'tags'")

        index = self._load_index()
        prompts = []

        for uuid_, data in index.items():
            if field == "tags":
                if any(query.lower() in tag.lower() for tag in data["tags"]):
                    prompts.append(self._data_to_prompt(uuid_, data))
            else:
                value = data.get(field, "")
                if value and query.lower() in value.lower():
                    prompts.append(self._data_to_prompt(uuid_, data))

        return prompts

    def _data_to_prompt(self, uuid: str, data: Dict) -> Prompt:
        """Convert raw data to a Prompt object.

        Args:
            uuid: The UUID of the prompt
            data: The raw data for the prompt

        Returns:
            Prompt: The prompt object
        """
        return Prompt(
            uuid=uuid,
            content=data["content"],
            description=data["description"],
            version=data["version"],
            tags=data["tags"],
            timestamp=data["updated_at"],
        )

    def merge(self, other: "PromptStore", override: bool = False):
        """Merge another PromptStore into this one.

        Args:
            other: The other PromptStore to merge
            override: If True, override existing prompts with those from the other store

        Raises:
            ReadOnlyStoreError: If the store is read-only
        """
        if self.readonly:
            raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

        our_index = self._load_index()
        their_index = other._load_index()

        for uuid_, their_data in their_index.items():
            if uuid not in our_index or override:
                our_index[uuid_] = their_data

        self._save_index(our_index)

    def __iter__(self) -> Iterator[Prompt]:
        """Iterate over all prompts in the store."""
        index = self._load_index()
        for uuid_, data in index.items():
            yield self._data_to_prompt(uuid_, data)

    def update(
        self,
        uuid: str,
        content: str,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
    ) -> Prompt:
        """Update an existing prompt with a new version.

        Args:
            uuid: The UUID of the prompt to update
            content: New content for the prompt
            description: New description for the prompt
            tags: New tags for the prompt

        Returns:
            Prompt: The updated prompt

        Raises:
            ReadOnlyStoreError: If the store is read-only
            PromptNotFoundError: If the prompt with the given UUID doesn't exist
        """
        if self.readonly:
            raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

        index = self._load_index()
        if uuid not in index:
            raise PromptNotFoundError(f"Prompt with UUID {uuid} not found")

        prompt_data = index[uuid]
        now = datetime.utcnow().isoformat()

        new_version = prompt_data["version"] + 1

        prompt_data["versions"].append(
            {
                "content": content,
                "description": description
                if description is not None
                else prompt_data["description"],
                "version": new_version,
                "created_at": now,
            }
        )

        prompt_data["content"] = content
        if description is not None:
            prompt_data["description"] = description
        if tags is not None:
            prompt_data["tags"] = tags
        prompt_data["version"] = new_version
        prompt_data["updated_at"] = now

        self._save_index(index)

        return Prompt(
            uuid=uuid,
            content=content,
            description=description
            if description is not None
            else prompt_data["description"],
            version=new_version,
            tags=tags if tags is not None else prompt_data["tags"],
            timestamp=now,
        )

    def get_online(
        self, uuid: str, url: str, version: int | None = None, folder: str = "prompts"
    ) -> Prompt:
        """Retrieve a prompt by its UUID from an online store.

        Args:
            uuid: The UUID of the prompt to retrieve
            url: The URL of the online store
            version: The version of the prompt to retrieve
            folder: The folder name to use for caching (defaults to "prompts")

        Returns:
            Prompt: The prompt object

        Raises:
            PromptNotFoundError: If the prompt with the given UUID doesn't exist
        """
        data = pystow.ensure_json(folder, url=url)

        if uuid not in data:
            raise PromptNotFoundError(
                f"Prompt with UUID {uuid} not found in online store"
            )

        prompt_data = data[uuid]

        if version:
            version_data = next(
                (v for v in prompt_data["versions"] if v["version"] == version), None
            )
            if not version_data:
                raise PromptNotFoundError(
                    f"Version {version} of prompt {uuid} not found in online store"
                )
            content = version_data["content"]
            description = version_data["description"]
            timestamp = version_data["created_at"]
        else:
            content = prompt_data["content"]
            description = prompt_data["description"]
            timestamp = prompt_data["updated_at"]

        return Prompt(
            uuid=uuid,
            content=content,
            description=description,
            version=prompt_data["version"],
            tags=prompt_data["tags"],
            timestamp=timestamp,
        )

__init__(location, readonly=False)

Initialize a new PromptStore.

Parameters:

Name Type Description Default
location Union[str, Path]

Path to the directory where prompts will be stored

required
readonly bool

If True, the store will be read-only

False
Source code in src/promptstore/store.py
def __init__(self, location: Union[str, Path], readonly: bool = False):
    """Initialize a new PromptStore.

    Args:
        location: Path to the directory where prompts will be stored
        readonly: If True, the store will be read-only
    """
    self.location = Path(location)
    self.readonly = readonly
    self.index_path = self.location / "index.json"
    self._init_storage()

__iter__()

Iterate over all prompts in the store.

Source code in src/promptstore/store.py
def __iter__(self) -> Iterator[Prompt]:
    """Iterate over all prompts in the store."""
    index = self._load_index()
    for uuid_, data in index.items():
        yield self._data_to_prompt(uuid_, data)

add(content, description=None, tags=None)

Add a new prompt to the store.

Parameters:

Name Type Description Default
content str

The content of the prompt

required
description Optional[str]

A description of the prompt

None
tags Optional[List[str]]

A list of tags for the prompt

None

Returns:

Name Type Description
Prompt Prompt

The newly created prompt

Source code in src/promptstore/store.py
def add(
    self,
    content: str,
    description: Optional[str] = None,
    tags: Optional[List[str]] = None,
) -> Prompt:
    """Add a new prompt to the store.

    Args:
        content: The content of the prompt
        description: A description of the prompt
        tags: A list of tags for the prompt

    Returns:
        Prompt: The newly created prompt
    """
    if self.readonly:
        raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

    prompt_uuid = str(uuid.uuid4())
    now = datetime.utcnow().isoformat()

    prompt_data = {
        "uuid": prompt_uuid,
        "content": content,
        "description": description,
        "version": 1,
        "versions": [
            {
                "content": content,
                "description": description,
                "version": 1,
                "created_at": now,
            }
        ],
        "tags": tags or [],
        "created_at": now,
        "updated_at": now,
    }

    index = self._load_index()
    index[prompt_uuid] = prompt_data
    self._save_index(index)

    return Prompt(
        uuid=prompt_uuid,
        content=content,
        description=description,
        version=1,
        tags=tags or [],
        timestamp=now,
    )

find(query, field='description')

Search for prompts based on a query.

Parameters:

Name Type Description Default
query str

The search query

required
field str

The field to search in (description, content, or tags)

'description'

Returns:

Type Description
List[Prompt]

List[Prompt]: A list of prompts matching the query

Source code in src/promptstore/store.py
def find(self, query: str, field: str = "description") -> List[Prompt]:
    """Search for prompts based on a query.

    Args:
        query: The search query
        field: The field to search in (description, content, or tags)

    Returns:
        List[Prompt]: A list of prompts matching the query
    """
    if field not in ("description", "content", "tags"):
        raise ValueError("Field must be 'description', 'content', or 'tags'")

    index = self._load_index()
    prompts = []

    for uuid_, data in index.items():
        if field == "tags":
            if any(query.lower() in tag.lower() for tag in data["tags"]):
                prompts.append(self._data_to_prompt(uuid_, data))
        else:
            value = data.get(field, "")
            if value and query.lower() in value.lower():
                prompts.append(self._data_to_prompt(uuid_, data))

    return prompts

from_dict(prompts, readonly=True) classmethod

Create a PromptStore from a dictionary.

Parameters:

Name Type Description Default
prompts Dict

Dictionary of prompt data

required
readonly bool

If True, the store will be read-only

True

Returns:

Name Type Description
PromptStore PromptStore

A prompt store initialized with the given prompts

Source code in src/promptstore/store.py
@classmethod
def from_dict(cls, prompts: Dict, readonly: bool = True) -> "PromptStore":
    """Create a PromptStore from a dictionary.

    Args:
        prompts: Dictionary of prompt data
        readonly: If True, the store will be read-only

    Returns:
        PromptStore: A prompt store initialized with the given prompts
    """
    import tempfile

    temp_dir = Path(tempfile.mkdtemp())
    store = cls(temp_dir, readonly=readonly)

    with open(store.index_path, "w", encoding="utf-8") as f:
        json.dump(prompts, f, indent=2, ensure_ascii=False)

    return store

from_file(path, readonly=True) classmethod

Create a PromptStore from a JSON file.

Parameters:

Name Type Description Default
path Union[str, Path]

Path to the JSON file containing prompts

required
readonly bool

If True, the store will be read-only

True

Returns:

Name Type Description
PromptStore PromptStore

A prompt store initialized with the prompts from the file

Source code in src/promptstore/store.py
@classmethod
def from_file(cls, path: Union[str, Path], readonly: bool = True) -> "PromptStore":
    """Create a PromptStore from a JSON file.

    Args:
        path: Path to the JSON file containing prompts
        readonly: If True, the store will be read-only

    Returns:
        PromptStore: A prompt store initialized with the prompts from the file
    """
    path = Path(path)
    if not path.exists():
        raise FileNotFoundError(f"Prompt file not found: {path}")

    if path.is_file():
        # If it's a single JSON file
        with open(path, "r", encoding="utf-8") as f:
            prompts = json.load(f)
    else:
        # If it's a directory containing an index.json
        index_path = path / "index.json"
        if not index_path.exists():
            raise FileNotFoundError(f"No index.json found in directory: {path}")
        with open(index_path, "r", encoding="utf-8") as f:
            prompts = json.load(f)

    return cls.from_dict(prompts, readonly=readonly)

get(uuid, version=None)

Retrieve a prompt by its UUID.

Parameters:

Name Type Description Default
uuid str

The UUID of the prompt to retrieve

required
version Optional[int]

The version of the prompt to retrieve

None

Returns:

Name Type Description
Prompt Prompt

The prompt object

Source code in src/promptstore/store.py
def get(self, uuid: str, version: Optional[int] = None) -> Prompt:
    """Retrieve a prompt by its UUID.

    Args:
        uuid: The UUID of the prompt to retrieve
        version: The version of the prompt to retrieve

    Returns:
        Prompt: The prompt object
    """
    index = self._load_index()
    if uuid not in index:
        raise PromptNotFoundError(f"Prompt with UUID {uuid} not found")

    prompt_data = index[uuid]

    if version:
        version_data = next(
            (v for v in prompt_data["versions"] if v["version"] == version), None
        )
        if not version_data:
            raise PromptNotFoundError(
                f"Version {version} of prompt {uuid} not found"
            )
        content = version_data["content"]
        description = version_data["description"]
        timestamp = version_data["created_at"]
    else:
        content = prompt_data["content"]
        description = prompt_data["description"]
        timestamp = prompt_data["updated_at"]

    return Prompt(
        uuid=uuid,
        content=content,
        description=description,
        version=prompt_data["version"],
        tags=prompt_data["tags"],
        timestamp=timestamp,
    )

get_online(uuid, url, version=None, folder='prompts')

Retrieve a prompt by its UUID from an online store.

Parameters:

Name Type Description Default
uuid str

The UUID of the prompt to retrieve

required
url str

The URL of the online store

required
version int | None

The version of the prompt to retrieve

None
folder str

The folder name to use for caching (defaults to "prompts")

'prompts'

Returns:

Name Type Description
Prompt Prompt

The prompt object

Raises:

Type Description
PromptNotFoundError

If the prompt with the given UUID doesn't exist

Source code in src/promptstore/store.py
def get_online(
    self, uuid: str, url: str, version: int | None = None, folder: str = "prompts"
) -> Prompt:
    """Retrieve a prompt by its UUID from an online store.

    Args:
        uuid: The UUID of the prompt to retrieve
        url: The URL of the online store
        version: The version of the prompt to retrieve
        folder: The folder name to use for caching (defaults to "prompts")

    Returns:
        Prompt: The prompt object

    Raises:
        PromptNotFoundError: If the prompt with the given UUID doesn't exist
    """
    data = pystow.ensure_json(folder, url=url)

    if uuid not in data:
        raise PromptNotFoundError(
            f"Prompt with UUID {uuid} not found in online store"
        )

    prompt_data = data[uuid]

    if version:
        version_data = next(
            (v for v in prompt_data["versions"] if v["version"] == version), None
        )
        if not version_data:
            raise PromptNotFoundError(
                f"Version {version} of prompt {uuid} not found in online store"
            )
        content = version_data["content"]
        description = version_data["description"]
        timestamp = version_data["created_at"]
    else:
        content = prompt_data["content"]
        description = prompt_data["description"]
        timestamp = prompt_data["updated_at"]

    return Prompt(
        uuid=uuid,
        content=content,
        description=description,
        version=prompt_data["version"],
        tags=prompt_data["tags"],
        timestamp=timestamp,
    )

merge(other, override=False)

Merge another PromptStore into this one.

Parameters:

Name Type Description Default
other PromptStore

The other PromptStore to merge

required
override bool

If True, override existing prompts with those from the other store

False

Raises:

Type Description
ReadOnlyStoreError

If the store is read-only

Source code in src/promptstore/store.py
def merge(self, other: "PromptStore", override: bool = False):
    """Merge another PromptStore into this one.

    Args:
        other: The other PromptStore to merge
        override: If True, override existing prompts with those from the other store

    Raises:
        ReadOnlyStoreError: If the store is read-only
    """
    if self.readonly:
        raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

    our_index = self._load_index()
    their_index = other._load_index()

    for uuid_, their_data in their_index.items():
        if uuid not in our_index or override:
            our_index[uuid_] = their_data

    self._save_index(our_index)

update(uuid, content, description=None, tags=None)

Update an existing prompt with a new version.

Parameters:

Name Type Description Default
uuid str

The UUID of the prompt to update

required
content str

New content for the prompt

required
description Optional[str]

New description for the prompt

None
tags Optional[List[str]]

New tags for the prompt

None

Returns:

Name Type Description
Prompt Prompt

The updated prompt

Raises:

Type Description
ReadOnlyStoreError

If the store is read-only

PromptNotFoundError

If the prompt with the given UUID doesn't exist

Source code in src/promptstore/store.py
def update(
    self,
    uuid: str,
    content: str,
    description: Optional[str] = None,
    tags: Optional[List[str]] = None,
) -> Prompt:
    """Update an existing prompt with a new version.

    Args:
        uuid: The UUID of the prompt to update
        content: New content for the prompt
        description: New description for the prompt
        tags: New tags for the prompt

    Returns:
        Prompt: The updated prompt

    Raises:
        ReadOnlyStoreError: If the store is read-only
        PromptNotFoundError: If the prompt with the given UUID doesn't exist
    """
    if self.readonly:
        raise ReadOnlyStoreError("Cannot modify a read-only prompt store")

    index = self._load_index()
    if uuid not in index:
        raise PromptNotFoundError(f"Prompt with UUID {uuid} not found")

    prompt_data = index[uuid]
    now = datetime.utcnow().isoformat()

    new_version = prompt_data["version"] + 1

    prompt_data["versions"].append(
        {
            "content": content,
            "description": description
            if description is not None
            else prompt_data["description"],
            "version": new_version,
            "created_at": now,
        }
    )

    prompt_data["content"] = content
    if description is not None:
        prompt_data["description"] = description
    if tags is not None:
        prompt_data["tags"] = tags
    prompt_data["version"] = new_version
    prompt_data["updated_at"] = now

    self._save_index(index)

    return Prompt(
        uuid=uuid,
        content=content,
        description=description
        if description is not None
        else prompt_data["description"],
        version=new_version,
        tags=tags if tags is not None else prompt_data["tags"],
        timestamp=now,
    )

Prompt

promptstore.prompt.Prompt

Source code in src/promptstore/prompt.py
class Prompt:
    def __init__(
        self,
        uuid: str,
        content: str,
        version: int,
        description: Optional[str] = None,
        tags: Optional[List[str]] = None,
        timestamp: Optional[str] = None,
    ):
        self.uuid = uuid
        self.content = content
        self.description = description
        self.version = version
        self.tags = tags or []
        self.timestamp = timestamp
        self._template = Template(content)
        self.variables = self._extract_variables(content)

    def fill(self, variables: dict) -> str:
        """Fill the prompt template with provided variables."""
        return self._template.render(**variables)

    def _extract_variables(self, content: str) -> List[str]:
        """Extract variable names from the template content."""
        env = Environment()
        ast = env.parse(content)
        variables = meta.find_undeclared_variables(ast)
        return sorted(list(variables))  # Convert set to sorted list

    def get_variables(self) -> List[str]:
        """Return the list of variable names in the template."""
        return self.variables

fill(variables)

Fill the prompt template with provided variables.

Source code in src/promptstore/prompt.py
def fill(self, variables: dict) -> str:
    """Fill the prompt template with provided variables."""
    return self._template.render(**variables)

get_variables()

Return the list of variable names in the template.

Source code in src/promptstore/prompt.py
def get_variables(self) -> List[str]:
    """Return the list of variable names in the template."""
    return self.variables

Exceptions

promptstore.exceptions

PromptNotFoundError

Bases: Exception

Raised when a prompt with the given UUID is not found.

Source code in src/promptstore/exceptions.py
1
2
3
4
class PromptNotFoundError(Exception):
    """Raised when a prompt with the given UUID is not found."""

    pass

ReadOnlyStoreError

Bases: Exception

Raised when attempting to modify a read-only store.

Source code in src/promptstore/exceptions.py
class ReadOnlyStoreError(Exception):
    """Raised when attempting to modify a read-only store."""

    pass