diff --git a/runtime/prompty/README.md b/runtime/prompty/README.md index ae4a690..df6b7f4 100644 --- a/runtime/prompty/README.md +++ b/runtime/prompty/README.md @@ -158,7 +158,7 @@ def get_response(customerId, prompt): ``` -In this case, whenever this code is executed, a `.ptrace` file will be created in the `path/to/output` directory. This file will contain the trace of the execution of the `get_response` function, the execution of the `get_customer` function, and the prompty internals that generated the response. +In this case, whenever this code is executed, a `.tracy` file will be created in the `path/to/output` directory. This file will contain the trace of the execution of the `get_response` function, the execution of the `get_customer` function, and the prompty internals that generated the response. ## OpenTelemetry Tracing You can add OpenTelemetry tracing to your application using the same hook mechanism. In your application, you might create something like `trace_span` to trace the execution of your prompts: @@ -187,10 +187,10 @@ This will produce spans during the execution of the prompt that can be sent to a The Prompty runtime also comes with a CLI tool that allows you to run prompts from the command line. The CLI tool is installed with the Python package. ```bash -prompty -s path/to/prompty/file +prompty -s path/to/prompty/file -e .env ``` -This will execute the prompt and print the response to the console. It also has default tracing enabled. +This will execute the prompt and print the response to the console. If there are any environment variables the CLI should take into account, you can pass those in via the `-e` flag. It also has default tracing enabled. ## Contributing We welcome contributions to the Prompty project! This community led project is open to all contributors. The project cvan be found on [GitHub](https://github.com/Microsoft/prompty). diff --git a/runtime/prompty/prompty/azure/executor.py b/runtime/prompty/prompty/azure/executor.py index e419edc..c03338a 100644 --- a/runtime/prompty/prompty/azure/executor.py +++ b/runtime/prompty/prompty/azure/executor.py @@ -91,7 +91,7 @@ def invoke(self, data: any) -> any: elif self.api == "completion": trace("signature", "AzureOpenAI.completions.create") args = { - "prompt": data.item, + "prompt": data, "model": self.deployment, **self.parameters, } @@ -111,10 +111,22 @@ def invoke(self, data: any) -> any: trace("result", response) elif self.api == "image": - raise NotImplementedError("Azure OpenAI Image API is not implemented yet") + trace("signature", "AzureOpenAI.images.generate") + args = { + "prompt": data, + "model": self.deployment, + **self.parameters, + } + trace("inputs", args) + response = client.images.generate.create(**args) + trace("result", response) # stream response if isinstance(response, Iterator): - return PromptyStream("AzureOpenAIExecutor", response) + if self.api == "chat": + # TODO: handle the case where there might be no usage in the stream + return PromptyStream("AzureOpenAIExecutor", response) + else: + return PromptyStream("AzureOpenAIExecutor", response) else: return response diff --git a/runtime/prompty/prompty/azure/processor.py b/runtime/prompty/prompty/azure/processor.py index e60adaa..f8314e2 100644 --- a/runtime/prompty/prompty/azure/processor.py +++ b/runtime/prompty/prompty/azure/processor.py @@ -1,5 +1,6 @@ from typing import Iterator from openai.types.completion import Completion +from openai.types.images_response import ImagesResponse from openai.types.chat.chat_completion import ChatCompletion from ..core import Invoker, InvokerFactory, Prompty, PromptyStream, ToolCall from openai.types.create_embedding_response import CreateEmbeddingResponse @@ -50,6 +51,17 @@ def invoke(self, data: any) -> any: return data.data[0].embedding else: return [item.embedding for item in data.data] + elif isinstance(data, ImagesResponse): + self.prompty.model.parameters + item: ImagesResponse = data + + if len(data.data) == 0: + raise ValueError("Invalid data") + elif len(data.data) == 1: + return data.data[0].url if item.data[0].url else item.data[0].b64_json + else: + return [item.url if item.url else item.b64_json for item in data.data] + elif isinstance(data, Iterator): def generator(): diff --git a/runtime/prompty/prompty/core.py b/runtime/prompty/prompty/core.py index 47f4791..6c5f708 100644 --- a/runtime/prompty/prompty/core.py +++ b/runtime/prompty/prompty/core.py @@ -561,7 +561,9 @@ async def __anext__(self): # StopIteration is raised # contents are exhausted if len(self.items) > 0: - with Tracer.start(f"{self.name}.AsyncPromptyStream") as trace: + with Tracer.start("AsyncPromptyStream") as trace: + trace("signature", f"{self.name}.AsyncPromptyStream") + trace("inputs", "None") trace("result", [to_dict(s) for s in self.items]) raise StopIteration diff --git a/runtime/prompty/prompty/tracer.py b/runtime/prompty/prompty/tracer.py index 6eaa5cb..3b31602 100644 --- a/runtime/prompty/prompty/tracer.py +++ b/runtime/prompty/prompty/tracer.py @@ -1,6 +1,7 @@ import os import json import inspect +import traceback import importlib import contextlib from pathlib import Path @@ -176,7 +177,8 @@ async def wrapper(*args, **kwargs): "result", { "exception": { - "type": type(e).__name__, + "type": type(e), + "traceback": traceback.format_tb(), "message": str(e), "args": to_dict(e.args), } diff --git a/runtime/prompty/tests/test_tracing.py b/runtime/prompty/tests/test_tracing.py index e13858d..fbdfc4a 100644 --- a/runtime/prompty/tests/test_tracing.py +++ b/runtime/prompty/tests/test_tracing.py @@ -151,5 +151,8 @@ def test_streaming(): result = prompty.execute( "prompts/streaming.prompty", ) + r = [] for item in result: - print(item) + r.append(item) + + return ' '.join(r)