Skip to content

Commit

Permalink
Merge pull request #164 from microsoft/python
Browse files Browse the repository at this point in the history
python 3.9 support
  • Loading branch information
sethjuarez authored Jan 14, 2025
2 parents 813b003 + 4231887 commit aad8bc2
Show file tree
Hide file tree
Showing 33 changed files with 521 additions and 320 deletions.
43 changes: 43 additions & 0 deletions .github/workflows/prompty-python-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: prompty Python build and test
on:
pull_request:
paths:
- 'runtime/prompty/**'


env:
AZURE_OPENAI_ENDPOINT: https://fake
AZURE_OPENAI_KEY: 12342323433

jobs:
prompty-tests:
name: run unit tests on supported python versions
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
os: [ubuntu-latest, macOS-latest, windows-latest]
permissions:
# This permission is needed for private repositories.
contents: read
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- uses: actions/checkout@v4

- uses: pdm-project/setup-pdm@v4
with:
python-version: ${{ matrix.python-version }}

- name: install dependencies
working-directory: ./runtime/prompty
run: pdm install

- name: test package
working-directory: ./runtime/prompty
run: pdm run pytest

- name: mypy check
working-directory: ./runtime/prompty
run: pdm run mypy . --check-untyped-defs

30 changes: 25 additions & 5 deletions .github/workflows/prompty-python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ env:
AZURE_OPENAI_ENDPOINT: https://fake
AZURE_OPENAI_KEY: 12342323433


jobs:
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
prompty-tests:
name: run unit tests on supported python versions
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
os: [ubuntu-latest, macOS-latest, windows-latest]
permissions:
# This permission is needed for private repositories.
contents: read
Expand All @@ -28,7 +31,7 @@ jobs:

- uses: pdm-project/setup-pdm@v4
with:
python-version: 3.11.5
python-version: ${{ matrix.python-version }}

- name: install dependencies
working-directory: ./runtime/prompty
Expand All @@ -38,6 +41,23 @@ jobs:
working-directory: ./runtime/prompty
run: pdm run pytest

- name: mypy check
working-directory: ./runtime/prompty
run: pdm run mypy . --check-untyped-defs

pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest

steps:
- uses: pdm-project/setup-pdm@v4
with:
python-version: 3.9

- name: install dependencies
working-directory: ./runtime/prompty
run: pdm install

- name: Publish package distributions to PyPI
working-directory: ./runtime/prompty
run: pdm publish
6 changes: 5 additions & 1 deletion runtime/prompty/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,8 @@ pdm.lock

# trace
trace.json
.runs/
.runs/


# ruff
.ruff_cache/
2 changes: 2 additions & 0 deletions runtime/prompty/doc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from inspect import getmembers, isclass, isfunction

import prompty


def build():
fn = [
f for f in getmembers(prompty, isfunction) if f[1].__module__.startswith("prompty")
Expand Down
62 changes: 33 additions & 29 deletions runtime/prompty/prompty/__init__.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
import traceback
import typing
from pathlib import Path
from typing import Dict, List, Union
from .tracer import trace
from .invoker import InvokerFactory, NoOp
from typing import Union

from .core import (
ModelSettings,
Prompty,
PropertySettings,
TemplateSettings,
param_hoisting,
)
from .invoker import InvokerFactory
from .parsers import PromptyChatParser
from .renderers import Jinja2Renderer
from .tracer import trace
from .utils import (
load_global_config,
load_global_config_async,
load_prompty_async,
load_prompty,
load_prompty_async,
)

from .renderers import *
from .parsers import *
InvokerFactory.add_renderer("jinja2", Jinja2Renderer)
InvokerFactory.add_parser("prompty.chat", PromptyChatParser)


@trace(description="Create a headless prompty object for programmatic use.")
def headless(
api: str,
content: str | List[str] | dict,
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
content: Union[str, list[str], dict],
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
connection: str = "default",
) -> Prompty:
"""Create a headless prompty object for programmatic use.
Expand Down Expand Up @@ -81,9 +85,9 @@ def headless(
@trace(description="Create a headless prompty object for programmatic use.")
async def headless_async(
api: str,
content: str | List[str] | dict,
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
content: Union[str, list[str], dict],
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
connection: str = "default",
) -> Prompty:
"""Create a headless prompty object for programmatic use.
Expand Down Expand Up @@ -188,17 +192,17 @@ def _load_raw_prompty(attributes: dict, content: str, p: Path, global_config: di
else:
outputs = {}

p = Prompty(
**attributes,
prompty = Prompty(
model=model,
inputs=inputs,
outputs=outputs,
template=template,
content=content,
file=p,
**attributes
)

return p
return prompty


@trace(description="Load a prompty file.")
Expand Down Expand Up @@ -311,7 +315,7 @@ async def load_async(prompty_file: str, configuration: str = "default") -> Promp
@trace(description="Prepare the inputs for the prompt.")
def prepare(
prompt: Prompty,
inputs: Dict[str, any] = {},
inputs: dict[str, typing.Any] = {},
):
"""Prepare the inputs for the prompt.
Expand Down Expand Up @@ -345,7 +349,7 @@ def prepare(
@trace(description="Prepare the inputs for the prompt.")
async def prepare_async(
prompt: Prompty,
inputs: Dict[str, any] = {},
inputs: dict[str, typing.Any] = {},
):
"""Prepare the inputs for the prompt.
Expand Down Expand Up @@ -379,9 +383,9 @@ async def prepare_async(
@trace(description="Run the prepared Prompty content against the model.")
def run(
prompt: Prompty,
content: dict | list | str,
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
content: Union[dict, list, str],
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
raw: bool = False,
):
"""Run the prepared Prompty content.
Expand Down Expand Up @@ -431,9 +435,9 @@ def run(
@trace(description="Run the prepared Prompty content against the model.")
async def run_async(
prompt: Prompty,
content: dict | list | str,
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
content: Union[dict, list, str],
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
raw: bool = False,
):
"""Run the prepared Prompty content.
Expand Down Expand Up @@ -483,9 +487,9 @@ async def run_async(
@trace(description="Execute a prompty")
def execute(
prompt: Union[str, Prompty],
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
inputs: Dict[str, any] = {},
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
inputs: dict[str, typing.Any] = {},
raw: bool = False,
config_name: str = "default",
):
Expand Down Expand Up @@ -537,9 +541,9 @@ def execute(
@trace(description="Execute a prompty")
async def execute_async(
prompt: Union[str, Prompty],
configuration: Dict[str, any] = {},
parameters: Dict[str, any] = {},
inputs: Dict[str, any] = {},
configuration: dict[str, typing.Any] = {},
parameters: dict[str, typing.Any] = {},
inputs: dict[str, typing.Any] = {},
raw: bool = False,
config_name: str = "default",
):
Expand Down
4 changes: 2 additions & 2 deletions runtime/prompty/prompty/azure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
from prompty.invoker import InvokerException

try:
from .executor import AzureOpenAIExecutor
from .processor import AzureOpenAIProcessor
from .executor import AzureOpenAIExecutor # noqa
from .processor import AzureOpenAIProcessor # noqa
except ImportError:
raise InvokerException(
"Error registering AzureOpenAIExecutor and AzureOpenAIProcessor", "azure"
Expand Down
42 changes: 23 additions & 19 deletions runtime/prompty/prompty/azure/executor.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import json
import azure.identity
import importlib.metadata
from typing import AsyncIterator, Iterator
from openai import APIResponse, AzureOpenAI, AsyncAzureOpenAI
import typing
from collections.abc import AsyncIterator, Iterator

from prompty.tracer import Tracer, sanitize
from ..core import AsyncPromptyStream, Prompty, PromptyStream
import azure.identity
from openai import APIResponse, AsyncAzureOpenAI, AzureOpenAI
from openai.types.chat.chat_completion import ChatCompletion

from prompty.tracer import Tracer

from ..core import AsyncPromptyStream, Prompty, PromptyStream
from ..invoker import Invoker, InvokerFactory

VERSION = importlib.metadata.version("prompty")
Expand All @@ -29,7 +31,10 @@ def __init__(self, prompty: Prompty) -> None:
if "api_key" not in self.kwargs:
# managed identity if client id
if "client_id" in self.kwargs:
default_credential = azure.identity.ManagedIdentityCredential(
default_credential: typing.Union[
azure.identity.ManagedIdentityCredential,
azure.identity.DefaultAzureCredential,
] = azure.identity.ManagedIdentityCredential(
client_id=self.kwargs.pop("client_id"),
)
# default credential
Expand All @@ -48,7 +53,7 @@ def __init__(self, prompty: Prompty) -> None:
self.deployment = self.prompty.model.configuration["azure_deployment"]
self.parameters = self.prompty.model.parameters

def invoke(self, data: any) -> any:
def invoke(self, data: typing.Any) -> typing.Union[str, PromptyStream]:
"""Invoke the Azure OpenAI API
Parameters
Expand Down Expand Up @@ -89,12 +94,11 @@ def invoke(self, data: any) -> any:
}
trace("inputs", args)

if "stream" in args and args["stream"] == True:
if "stream" in args and args["stream"]:
response = client.chat.completions.create(**args)
else:
raw: APIResponse = client.chat.completions.with_raw_response.create(
**args
)
raw = client.chat.completions.with_raw_response.create(**args)

response = ChatCompletion.model_validate_json(raw.text)

for k, v in raw.headers.raw:
Expand Down Expand Up @@ -135,7 +139,7 @@ def invoke(self, data: any) -> any:
**self.parameters,
}
trace("inputs", args)
response = client.images.generate.create(**args)
response = client.images.generate(**args)
trace("result", response)

# stream response
Expand All @@ -148,7 +152,7 @@ def invoke(self, data: any) -> any:
else:
return response

async def invoke_async(self, data: str) -> str:
async def invoke_async(self, data: str) -> typing.Union[str, AsyncPromptyStream]:
"""Invoke the Prompty Chat Parser (Async)
Parameters
Expand Down Expand Up @@ -188,13 +192,13 @@ async def invoke_async(self, data: str) -> str:
}
trace("inputs", args)

if "stream" in args and args["stream"] == True:
if "stream" in args and args["stream"]:
response = await client.chat.completions.create(**args)
else:
raw: APIResponse = await client.chat.completions.with_raw_response.create(
**args
raw: APIResponse = (
await client.chat.completions.with_raw_response.create(**args)
)
response = ChatCompletion.model_validate_json(raw.text)
response = ChatCompletion.model_validate_json(raw.text())
for k, v in raw.headers.raw:
trace(k.decode("utf-8"), v.decode("utf-8"))

Expand Down Expand Up @@ -234,7 +238,7 @@ async def invoke_async(self, data: str) -> str:
**self.parameters,
}
trace("inputs", args)
response = await client.images.generate.create(**args)
response = await client.images.generate(**args)
trace("result", response)

# stream response
Expand Down
Loading

0 comments on commit aad8bc2

Please sign in to comment.