From 4337ee5f763093c45cc66db3b100a696d4a8c2c7 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Mon, 15 Jan 2024 23:51:30 -0500 Subject: [PATCH] Updating tutorial --- docs/welcome/installation.md | 4 + .../{quickstart.md => quickstart_old.md} | 2 + docs/welcome/tutorial.md | 173 ++++++++++++++++++ mkdocs.yml | 4 +- src/marvin/client/openai.py | 37 ++-- src/marvin/settings.py | 22 +-- 6 files changed, 210 insertions(+), 32 deletions(-) rename docs/welcome/{quickstart.md => quickstart_old.md} (99%) create mode 100644 docs/welcome/tutorial.md diff --git a/docs/welcome/installation.md b/docs/welcome/installation.md index c8323006c..f39eae4ea 100644 --- a/docs/welcome/installation.md +++ b/docs/welcome/installation.md @@ -14,6 +14,10 @@ Upgrade to the latest released version at any time: pip install marvin -U ``` +## Tutorial + +Now that you've installed Marvin, check out the [tutorial](tutorial.md) to learn how to use it. + ## Requirements Marvin requires Python 3.9 or greater, and is tested on all major Python versions and operating systems. diff --git a/docs/welcome/quickstart.md b/docs/welcome/quickstart_old.md similarity index 99% rename from docs/welcome/quickstart.md rename to docs/welcome/quickstart_old.md index f3c0b4ae7..31ad46682 100644 --- a/docs/welcome/quickstart.md +++ b/docs/welcome/quickstart_old.md @@ -1,5 +1,7 @@ # Quickstart +0-60 in 60 seconds. + After [installing Marvin](../installation), the fastest way to get started is by using one of Marvin's high-level [AI components](../../components/overview). These components are designed to integrate AI into abstractions you already know well, creating the best possible opt-in developer experience. !!! info "Initializing a Client" diff --git a/docs/welcome/tutorial.md b/docs/welcome/tutorial.md new file mode 100644 index 000000000..0b4300451 --- /dev/null +++ b/docs/welcome/tutorial.md @@ -0,0 +1,173 @@ +# Tutorial + +![](/docs/assets/images/heroes/dont_panic.png){ width=500 } + +## Installing Marvin + +Before we can start, you'll need to [install Marvin](installation.md). Come back here when you're done! + +(Spoiler alert: run `pip install marvin -U`.) + +## Getting an OpenAI API Key + +Marvin uses OpenAI models to power all of its tools. In order to use Marvin, you'll need an OpenAI API key. + +You can create an API key [on the OpenAI platform ](https://platform.openai.com/api-keys). Once you've created it, set it as an environment variable called `OPENAI_API_KEY` (for any application on your machine to use) or `MARVIN_OPENAI_API_KEY` (if you only want Marvin to use it). In addition to setting it in your terminal, you can also write the variable to a dotenv file at `~/.marvin/.env`. + +For quick use, you can also pass your API key directly to Marvin at runtime. We do **NOT** recommend this for production: + +```python +import marvin +marvin.settings.openai.api_key = 'YOUR_API_KEY' +``` + +## Tools + +Marvin has a variety of tools that let you use LLMs to solve common but complex problems. In this tutorial, we'll try out a few of them to get a feel for how Marvin works. By the end of the tutorial, you'll have tried some of Marvin's advanced features and be ready to take on the universe! Just don't forget your towel. + +### 🏷️ Classification + +Classification is one of Marvin's most straightforward features. Given some text and a list of labels, Marvin will choose the label that best fits the text. The `classify` function is great for tasks like sentiment analysis, intent classification, routing, and more. + +!!! Example "True/False" + The simplest possible classification example maps the word "yes" to a boolean value like `True` or `False`: + + ```python + import marvin + + result = marvin.classify("yes", labels=bool) + ``` + + !!! success "Result" + ```python + assert result is True + ``` + +Now that you've seen the most basic example, let's try something a little more useful: + +!!! Example "Sentiment" + + This example performs a basic sentiment analysis on some text: + + ```python + import marvin + + result = marvin.classify( + "Marvin is so easy to use!", + labels=["positive", "negative"], + ) + ``` + + !!! success "Result" + ```python + assert result == "positive" + ``` + +This is a great example of how all Marvin tools should feel. Historically, classifying text was a major challenge for natural language processing frameworks. But with Marvin, it's as easy as calling a function. + +!!! tip "Structured labels" + For the `classify` function, you can supply labels as a `list` of labels, a `bool` type, an `Enum` class, or a `Literal` type. This gives you many options for returning structured (non-string) labels. + +### 🪄 Transformation + +Classification maps text to a single label, but what if you want to convert text to a more structured form? Marvin's `cast` function lets you do just that. Given some text and a target type, Marvin will return a structured representation of the text. + + +!!! example "Standardization" + + Suppose you ran a survey and one of the questions asked where people live. Marvin can convert their freeform responses to a structured `Location` type: + + ```python + import marvin + from pydantic import BaseModel + + class Location(BaseModel): + city: str + state: str + + location = marvin.cast("NYC", target=Location) + ``` + + !!! success "Result" + The string "NYC" was converted to a full `Location` object: + + ```python + assert location == Location(city="New York", state="New York") + ``` + +#### Instructions + +All Marvin functions have an `instructions` parameter that let you fine-tune their behavior with natural language. For example, you can use instructions to tell Marvin to extract a specific type of information from a text or to format a response in a specific way. + +Suppose you wanted to standardize the survey responses in the previous example, but instead of using a full Pydantic model, you wanted the result to still be a string. The `cast` function will accept `target=str`, but that's so general it's unlikely to do what you want without additional guidance. That's where instructions come in: + +!!! example "Instructions" + + Repeat the previous example, but cast to a string according to the instructions: + + ```python + import marvin + + location = marvin.cast( + "NYC", + target=str, + instructions="Return the proper city and state name", + ) + ``` + + !!! success "Result" + The result is a string that complies with the instructions: + + ```python + assert location == "New York, New York" + ``` + +### 🔍 Extraction + +The `extract` function is like a generalization of the `cast` function: instead of transforming the entire text to a single target type, it extracts a list of entities from the text. This is useful for identifying people, places, ratings, keywords, and more. + +!!! Example "Extraction" + + Suppose you wanted to extract the product features mentioned in a review: + + ```python + import marvin + + features = marvin.extract( + "I love my new phone's camera, but the battery life could be improved.", + instructions="extract product features", + ) + ``` + + !!! success "Result" + ```python + assert features == ["camera", "battery life"] + ``` + +The `extract` function can take a target type, just like `cast`. This lets you extract structured entities from text. For example, you could extract a list of `Location` objects from a text: + +!!! Example "Extract locations" + + ```python + import marvin + from pydantic import BaseModel + + class Location(BaseModel): + city: str + state: str + + locations = marvin.extract( + "They've got a game in NY, then they go to DC before Los Angeles.", + target=Location + ) + ``` + + !!! success "Result" + ```python + assert locations == [ + Location(city="New York", state="New York"), + Location(city="Washington", state="District of Columbia"), + Location(city="Los Angeles", state="California"), + ] + ``` + \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index a2def54d6..195e54796 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,8 +10,8 @@ nav: - welcome/what_is_marvin.md - Getting started: - - welcome/installation.md - - welcome/quickstart.md + - Installation: welcome/installation.md + - Tutorial: welcome/tutorial.md # - help/legacy_docs.md # - Overview: welcome/overview.md diff --git a/src/marvin/client/openai.py b/src/marvin/client/openai.py index 119d1ac8b..0fae5b453 100644 --- a/src/marvin/client/openai.py +++ b/src/marvin/client/openai.py @@ -1,3 +1,4 @@ +import inspect from functools import partial from pathlib import Path from typing import ( @@ -156,16 +157,19 @@ def _get_default_client(client_type: str) -> Union[Client, AsyncClient]: ) except AttributeError: raise ValueError( - "To use Azure OpenAI, please set all of the following environment" - " variables in `~/.marvin/.env`:" - "\n\n" - "```" - "\nMARVIN_USE_AZURE_OPENAI=true" - "\nMARVIN_AZURE_OPENAI_API_KEY=..." - "\nMARVIN_AZURE_OPENAI_API_VERSION=..." - "\nMARVIN_AZURE_OPENAI_ENDPOINT=..." - "\nMARVIN_AZURE_OPENAI_DEPLOYMENT_NAME=..." - "\n```" + inspect.cleandoc( + """ + To use Azure OpenAI, please set all of the following environment variables in `~/.marvin/.env`: + + ``` + MARVIN_USE_AZURE_OPENAI=true + MARVIN_AZURE_OPENAI_API_KEY=... + MARVIN_AZURE_OPENAI_API_VERSION=... + MARVIN_AZURE_OPENAI_ENDPOINT=... + MARVIN_AZURE_OPENAI_DEPLOYMENT_NAME=... + ``` + """ + ) ) api_key = ( @@ -173,8 +177,17 @@ def _get_default_client(client_type: str) -> Union[Client, AsyncClient]: ) if not api_key: raise ValueError( - "OpenAI API key not found. Please either set `MARVIN_OPENAI_API_KEY` in" - " `~/.marvin/.env` or otherwise set `OPENAI_API_KEY` in your environment." + inspect.cleandoc( + """ + OpenAI API key not found! Marvin will not work properly without it. + + You can either: + 1. Set the `MARVIN_OPENAI_API_KEY` or `OPENAI_API_KEY` environment variables + 2. Set `marvin.settings.openai.api_key` in your code (not recommended for production) + + If you do not have an OpenAI API key, you can create one at https://platform.openai.com/api-keys. + """ + ) ) if client_type not in ["sync", "async"]: raise ValueError(f"Invalid client type {client_type!r}") diff --git a/src/marvin/settings.py b/src/marvin/settings.py index d105bf507..6a7f339e1 100644 --- a/src/marvin/settings.py +++ b/src/marvin/settings.py @@ -1,14 +1,4 @@ -"""Settings for configuring `marvin`. - -## Requirements -All you ***need*** to configure is your OpenAI API key. - -You can set this in `~/.marvin/.env` or as an environment variable on your system: -``` -MARVIN_OPENAI_API_KEY=sk-... -``` ---- -""" +"""Settings for configuring `marvin`.""" import os from contextlib import contextmanager @@ -188,13 +178,9 @@ class OpenAISettings(MarvinSettings): @field_validator("api_key") def discover_api_key(cls, v): if v is None: - v = SecretStr(os.environ.get("OPENAI_API_KEY")) - if v.get_secret_value() is None: - raise ValueError( - "OpenAI API key not found. Please either set" - " `MARVIN_OPENAI_API_KEY` in `~/.marvin/.env` or otherwise set" - " `OPENAI_API_KEY` in your environment." - ) + # check global OpenAI API key + v = os.environ.get("OPENAI_API_KEY") + return v