Skip to content

Commit

Permalink
Merge pull request #256 from parea-ai/new-cookbook-example
Browse files Browse the repository at this point in the history
Fix OpenAI Error on API when API key is not needed.
Update cookbooks for OpenAI v >1
Add new cookbook example
  • Loading branch information
jalexanderII committed Dec 9, 2023
2 parents 4b7c5dd + 74840e6 commit 5e0a9cf
Show file tree
Hide file tree
Showing 9 changed files with 1,637 additions and 1,306 deletions.
42 changes: 25 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,21 @@ poetry add parea-ai

## Evaluating Your LLM App

You can evaluate any step of your LLM app by wrapping it with a decorator, called `trace`, and specifying the evaluation function(s).
The scores associated with the traces will be logged to the Parea [dashboard](https://app.parea.ai/logs) and/or in a local CSV file if you don't have a Parea API key.

Evaluation functions receive an argument `log` (of type [Log](parea/schemas/models.py)) and should return a
float between 0 (bad) and 1 (good) inclusive. You don't need to start from scratch, there are pre-defined evaluation functions for [general purpose](parea/evals/general.py),
You can evaluate any step of your LLM app by wrapping it with a decorator, called `trace`, and specifying the evaluation
function(s).
The scores associated with the traces will be logged to the Parea [dashboard](https://app.parea.ai/logs) and/or in a
local CSV file if you don't have a Parea API key.

Evaluation functions receive an argument `log` (of type [Log](parea/schemas/models.py)) and should return a
float between 0 (bad) and 1 (good) inclusive. You don't need to start from scratch, there are pre-defined evaluation
functions for [general purpose](parea/evals/general.py),
[chat](parea/evals/chat.py), [RAG](parea/evals/rag.py), and [summarization](parea/evals/summary.py) apps :)

You can define evaluation functions locally or use the ones you have deployed to Parea's [Test Hub](https://app.parea.ai/test-hub).
You can define evaluation functions locally or use the ones you have deployed to
Parea's [Test Hub](https://app.parea.ai/test-hub).
If you choose the latter option, the evaluation happens asynchronously and non-blocking.

A fully locally working cookbook can be found [here](parea/cookbook/tracing_and_evaluating_openai_endpoint.py).
A fully locally working cookbook can be found [here](parea/cookbook/tracing_and_evaluating_openai_endpoint.py).
Alternatively, you can add the following code to your codebase to get started:

```python
Expand All @@ -62,7 +66,8 @@ def function_to_evaluate(*args, **kwargs) -> ...:
## Debugging Chains & Agents

You can iterate on your chains & agents much faster by using a local cache. This will allow you to make changes to your
code & prompts without waiting for all previous, valid LLM responses. Simply add these two lines to the beginning your code and start
code & prompts without waiting for all previous, valid LLM responses. Simply add these two lines to the beginning your
code and start
[a local redis cache](https://redis.io/docs/getting-started/install-stack/):

```python
Expand All @@ -71,14 +76,15 @@ from parea import init, RedisCache
init(cache=RedisCache())
```

Above will use the default redis cache at `localhost:6379` with no password. You can also specify your redis database by:
Above will use the default redis cache at `localhost:6379` with no password. You can also specify your redis database
by:

```python
from parea import init, RedisCache

cache = RedisCache(
host=os.getenv("REDIS_HOST", "localhost"), # default value
port=int(os.getenv("REDIS_PORT", 6379)), # default value
port=int(os.getenv("REDIS_PORT", 6379)), # default value
password=os.getenv("REDIS_PASSWORT", None) # default value
)
init(cache=cache)
Expand All @@ -88,21 +94,24 @@ If you set `cache = None` for `init`, no cache will be used.

### Benchmark your LLM app across many inputs

You can benchmark your LLM app across many inputs by using the `benchmark` command. This will run your the entry point
You can benchmark your LLM app across many inputs by using the `benchmark` command. This will run your the entry point
of your app with the specified inputs and create a report with the results.

```bash
parea benchmark --func app:main --csv_path benchmark.csv
```

The CSV file will be used to fill in the arguments to your function. The report will be a CSV file of all the traces. If you
set your Parea API key, the traces will also be logged to the Parea dashboard. Note, for this feature you need to have a
The CSV file will be used to fill in the arguments to your function. The report will be a CSV file of all the traces. If
you
set your Parea API key, the traces will also be logged to the Parea dashboard. Note, for this feature you need to have a
redis cache running. Please, raise a GitHub issue if you would like to use this feature without a redis cache.

### Automatically log all your LLM call traces

You can automatically log all your LLM traces to the Parea dashboard by setting the `PAREA_API_KEY` environment variable or specifying it in the `init` function.
This will help you debug issues your customers are facing by stepping through the LLM call traces and recreating the issue
You can automatically log all your LLM traces to the Parea dashboard by setting the `PAREA_API_KEY` environment variable
or specifying it in the `init` function.
This will help you debug issues your customers are facing by stepping through the LLM call traces and recreating the
issue
in your local setup & code.

```python
Expand All @@ -114,7 +123,6 @@ init(
)
```


## Use a deployed prompt

```python
Expand Down Expand Up @@ -195,7 +203,7 @@ temperature = 0.0

# define your OpenAI call as you would normally and we'll automatically log the results
def main():
openai.ChatCompletion.create(model=model, temperature=temperature, messages=messages).choices[0].message["content"]
openai.chat.completions.create(model=model, temperature=temperature, messages=messages).choices[0].message.content
```

### Open source community features
Expand Down
Binary file added parea/cookbook/img/trace_log_view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion parea/cookbook/tracing_with_Parea_sdk.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
"\n",
"\n",
"# Non deployed version\n",
"# from parea.schemas.models import LLMInputs, Message, ModelParams, Role\n",
"# from parea.schemas.logs import LLMInputs, Message, ModelParams, Role\n",
"\n",
"\n",
"# def argument_generator(query: str, additional_description: str = \"\") -> str:\n",
Expand Down
279 changes: 279 additions & 0 deletions parea/cookbook/tracing_with_function_calling_and_chains.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"formattedRanges": [],
"cell_id": "d9114afabb54438ea6c7f68a68bf6fb5",
"deepnote_cell_type": "text-cell-h1"
},
"source": [
"# Tracing with Function Calls and Chains"
],
"block_group": "53e88a688fb843c381543ca9b45c9d76"
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"import json\n",
"import os\n",
"from typing import List, Dict, Any, Union\n",
"\n",
"from parea import Parea\n",
"from parea.schemas.log import LLMInputs, Message, ModelParams\n",
"from parea.schemas.models import Completion\n",
"from parea.utils.trace_utils import trace, trace_insert\n",
"\n",
"p = Parea(api_key=os.environ[\"PAREA_API_KEY\"])"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [],
"source": [
"LIMIT = 2 # limit any loops to 2 iterations for demo purposes\n",
"\n",
"COURSE_FUNCTIONS = [\n",
" {\n",
" \"name\": \"generate_course_outline\",\n",
" \"description\": \"Generates a course outline\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"Description\": {\"type\": \"string\", \"description\": \"The description of the course\"},\n",
" \"chapters\": {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"name\": {\"type\": \"string\", \"description\": \"The title of the chapter\"},\n",
" \"description\": {\"type\": \"string\", \"description\": \"The summary of the chapter\"},\n",
" },\n",
" \"required\": [\"name\", \"description\"],\n",
" },\n",
" \"description\": \"The chapters included in the course\",\n",
" \"minItems\": 1,\n",
" \"maxItems\": LIMIT,\n",
" },\n",
" },\n",
" \"required\": [\"Description\", \"chapters\"],\n",
" },\n",
" }\n",
"]\n",
"\n",
"CHAPTER_FUNCTIONS = [\n",
" {\n",
" \"name\": \"generate_chapter_outline\",\n",
" \"description\": \"Generates a chapter outline\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"name\": {\"type\": \"string\", \"description\": \"The title of the chapter\"},\n",
" \"description\": {\"type\": \"string\", \"description\": \"The summary of the chapter\"},\n",
" \"sections\": {\n",
" \"type\": \"array\",\n",
" \"items\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"name\": {\"type\": \"string\", \"description\": \"The title of the section\"},\n",
" \"description\": {\"type\": \"string\", \"description\": \"The summary of the section\"},\n",
" },\n",
" \"required\": [\"name\", \"description\"],\n",
" },\n",
" \"description\": \"The sections included in the chapter\",\n",
" \"minItems\": 1,\n",
" \"maxItems\": LIMIT,\n",
" },\n",
" },\n",
" \"required\": [\"name\", \"description\", \"sections\"],\n",
" },\n",
" }\n",
"]"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2023-12-09T19:18:33.559895Z",
"start_time": "2023-12-09T19:18:33.291059Z"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"USER = \"[email protected]\"\n",
"\n",
"\n",
"# Create a reusable call lmm helper function\n",
"# Parea SDK is automatically traced\n",
"def call_llm(messages: List[Dict[str, str]], name: str = \"LLM Call\") -> str:\n",
" return p.completion(\n",
" Completion(\n",
" llm_configuration=LLMInputs(\n",
" model=\"gpt-3.5-turbo-1106\",\n",
" model_params=ModelParams(temp=0.0, max_length=512),\n",
" messages=[Message(**m) for m in messages],\n",
" functions=COURSE_FUNCTIONS + CHAPTER_FUNCTIONS,\n",
" ),\n",
" end_user_identifier=USER,\n",
" trace_name=name,\n",
" )\n",
" ).content\n",
"\n",
"\n",
"# Helper function to get function call arguments if they exists\n",
"def get_function_call_or_content(response: str) -> Union[str, Dict[str, Any]]:\n",
" # Function calls are returned in triple backticks\n",
" parsed_response = response.replace(\"```\", \"\")\n",
" # return function call or content string\n",
" try:\n",
" return json.loads(parsed_response).get(\"arguments\")\n",
" except Exception as e:\n",
" return parsed_response"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"# PROMPTS\n",
"\n",
"\n",
"def get_course(topic: str):\n",
" _course_outline = call_llm(\n",
" messages=[{\"role\": \"user\", \"content\": f\"Generate a course outline on {topic}\"}],\n",
" name=f\"Create {topic} Course Outline\",\n",
" )\n",
" course_outline = get_function_call_or_content(_course_outline)\n",
" print(json.dumps(course_outline, indent=4))\n",
" return course_outline\n",
"\n",
"\n",
"# I want to group all llm calls into one trace,\n",
"# so I add the trace decorator to create a parent trace,\n",
"# each call_llm will be a child span\n",
"@trace(name=\"Get Chapters\")\n",
"def get_chapters(course_outline: Dict[str, str]):\n",
" chapter_outlines = course_outline.get(\"chapters\", [])\n",
" chapters = [\n",
" get_function_call_or_content(\n",
" call_llm(\n",
" messages=[{\"role\": \"user\", \"content\": f\"Generate a chapter outline on {chapter.get('name')}, with description {chapter.get('description')}\"}],\n",
" name=f\"Create Chapter {idx}\",\n",
" )\n",
" )\n",
" for idx, chapter in enumerate(chapter_outlines, start=1)\n",
" ]\n",
" return chapters[:LIMIT]\n",
"\n",
"\n",
"@trace\n",
"def get_sections(chapter: Dict[str, str]):\n",
" chapter_name = chapter.get(\"name\", \"section\")\n",
" # I want the trace name to be dynamic based on the chapter param,\n",
" # so I can use Parea trace_insert helper method to add dynamic data\n",
" trace_insert({\"trace_name\": f\"Get {chapter_name} Sections\"})\n",
"\n",
" section_outlines = chapter.get(\"sections\", [])\n",
" sections = [\n",
" get_function_call_or_content(\n",
" call_llm(\n",
" messages=[{\"role\": \"user\", \"content\": f\"\"\"Generate a section outline on {section['name']}, with description {section['description']}\"\"\"}],\n",
" name=f\"Create Section {idx}\",\n",
" )\n",
" )\n",
" for idx, section in enumerate(section_outlines, start=1)\n",
" ]\n",
" return sections[:LIMIT]"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"@trace(name=\"Get Sections\")\n",
"def get_all_sections(chapters: List[Dict[str, str]]):\n",
" return [get_sections(chapter) for chapter in chapters]\n",
"\n",
"\n",
"@trace\n",
"def run_creation(topic: str):\n",
" trace_insert({\"trace_name\": f\"Course on {topic}\"})\n",
" course_outline = get_course(topic)\n",
" chapters = get_chapters(course_outline)\n",
" sections = get_all_sections(chapters)\n",
" return sections\n",
"\n",
"\n",
"@trace(name=\"Generate Courses for Topics\")\n",
"def main(topics):\n",
" for topic in topics:\n",
" print(f\"\\n New Topic: {topic} \\n\")\n",
" run_creation(topic)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"main([\"Artificial Intelligence\", \"Machine Learning\", \"Deep Learning\"])"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"### Trace Log View in UI\n",
"\n",
"![TraceView](img/trace_log_view.png)"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"deepnote": {},
"orig_nbformat": 2,
"deepnote_notebook_id": "27ae7c4c406747b5b5936b15081cdaec",
"deepnote_execution_queue": [],
"kernelspec": {
"name": "python3",
"language": "python",
"display_name": "Python 3 (ipykernel)"
}
}
}
Loading

0 comments on commit 5e0a9cf

Please sign in to comment.