Create a Humanloop Account
If you haven't already, [create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to HumanloopAdd an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.Create a Humanloop Account
If you haven't already, [create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to HumanloopAdd an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.Create a Humanloop Account
1. [Create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to Humanloop 2. Get a Humanloop API key from [Organization Settings](https://app.humanloop.com/account/api-keys).Add an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.{JSON.stringify(p, null, 2)}) : p.type === "text" ? (
{p.text}
) : ({p.reasoning}
) )}{JSON.stringify(p, null, 2)}) : p.type === "text" ? (
{p.text}
) : ({p.reasoning}
) )}Create a Humanloop Account
1. [Create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to Humanloop 2. Get a Humanloop API key from [Organization Settings](https://app.humanloop.com/account/api-keys).Add an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.""", } ] def populate_template(template: list, inputs: dict[str, str]) -> list: """Populate a template with input variables.""" messages = [] for i, template_message in enumerate(template): content = template_message["content"] for key, value in inputs.items(): content = content.replace("{{" + key + "}}", value) message = {**template_message, "content": content} messages.append(message) return messages ``` Define the RAG Pipeline: ```python def retrieval_tool(question: str) -> str: """Retrieve most relevant document from the vector db (Chroma) for the question.""" response = collection.query(query_texts=[question], n_results=1) retrieved_doc = response["documents"][0][0] return retrieved_doc def call_llm(**inputs): # Populate the Prompt template messages = populate_template(template, inputs) # Call OpenAI to get response chat_completion = openai.chat.completions.create( model=model, temperature=temperature, messages=messages, ) return chat_completion.choices[0].message.content def ask_question(**inputs)-> str: """Ask a question and get an answer using a simple RAG pipeline""" # Retrieve context retrieved_data = retrieval_tool(inputs["question"]) inputs = {**inputs, "retrieved_data": retrieved_data} # Call LLM return call_llm(**inputs) ``` Run the pipeline: ```python output = ask_question( **{ "question": "A 34-year-old male suffers from inherited hemophilia A. He and his wife have three unaffected daughters. What is the probability that the second daughter is a carrier of the disease?", "option_A": "0%", "option_B": "25%", "option_C": "50%", "option_D": "75%", "option_E": "100%", } ) print(output) ``` ## Run an Evaluation Now we will integrate Humanloop into our RAG pipeline to evaluate it. We will use the Humanloop SDK to run an Eval on our RAG pipeline. Initialize the Humanloop SDK: ```python from humanloop import Humanloop load_dotenv() humanloop = Humanloop(api_key=os.getenv("HUMANLOOP_KEY")) ``` ### Set up Evaluators Our Dataset has ground truth answers we can compare against. It's very unlikely that the AI answers are *exactly* the same as the answers but we can measure how close they are by using the "Levenshtein distance" Evaluator. The code for this Evaluator is in the cookbook. We can run the Evaluator locally. However, if we upload it to Humanloop, we get the added benefit that Humanloop can run the Evalaution for us and this can be integrated into CI/CD. ```python def upload_evaluators(): """Uploads Evaluators to Humanloop. Uploads the "Exact match", "Levenshtein", and "Reasoning" Evaluators. The "Exact match" and "Levenshtein" Evaluators are slight modifications to the examples automatically created in the "Example Evaluators" folder in Humanloop when you signed up, with some additional parsing for the output of this RAG pipeline. """ # Upload Code Evaluators for evaluator_name, file_name, return_type in [ ("Exact match", "exact_match.py", "boolean"), ("Levenshtein", "levenshtein.py", "number"), ]: with open(f"../../assets/evaluators/{file_name}", "r") as f: code = f.read() humanloop.evaluators.upsert( path=f"Evals demo/{evaluator_name}", spec={ "evaluator_type": "python", "arguments_type": "target_required", "return_type": return_type, "code": code, }, commit_message=f"New version from {file_name}", ) # Upload an LLM Evaluator humanloop.evaluators.upsert( path="Evals demo/Reasoning", spec={ "evaluator_type": "llm", "arguments_type": "target_free", "return_type": "boolean", "prompt": { "model": "gpt-4o-mini", "endpoint": "complete", "temperature": 0, "template": "An answer is shown below. The answer contains 3 sections, separated by \"---\". The first section is the final answer. The second section is an explanation. The third section is a citation.\n\nEvaluate if the final answer follows from the citation and the reasoning in the explanation section. Give a brief explanation/discussion. Do not make your judgment based on factuality, but purely based on the logic presented.\nOn a new line, give a final verdict of \"True\" or \"False\".\n\nAnswer:\n{{log.output}}", }, }, commit_message="Initial reasoning evaluator.", ) upload_evaluators() ``` ### Create a Dataset We upload a test dataset to Humanloop: ```python def upload_dataset_to_humanloop(): df = pd.read_json("../../assets/datapoints.jsonl", lines=True) datapoints = [row.to_dict() for _i, row in df.iterrows()][0:20] return humanloop.datasets.upsert( path="Evals demo/MedQA test", datapoints=datapoints, commit_message=f"Added {len(datapoints)} datapoints from MedQA test dataset.", ) dataset = upload_dataset_to_humanloop() ``` ### Run Eval Now that we have our Flow, our Dataset and our Evaluators we can create and run an Evaluation. ```python checks = humanloop.evaluations.run( name="Demo cookbook", file={ "path": "Evals demo/MedQA pipeline", "callable": ask_question, }, dataset={ "path": "Evals demo/MedQA test", }, evaluators=[ {"path": "Evals demo/Exact match"}, {"path": "Evals demo/Levenshtein"}, {"path": "Evals demo/Reasoning"}, {"path": "Example Evaluators/Code/Latency"}, ], ) ``` ## Add detailed logging One limitation of our Evaluation so far is that we've measured the app end-to-end but we don't know how the different components contribute to performance. If we really want to improve our app, we'll need to log the full trace of events, including separate Tool and Prompt steps: We can do this by adding logging for the Prompt and Tool steps within the Flow using Humanloop's Python decorators. If you're using a different language, you can still log to Humanloop via the API. Skip to the "Logging with the API" section below or check out our [guide](https://humanloop.com/docs/v5/guides/observability/logging-through-api) for more details. ```python @humanloop.tool(path="Evals demo/Retrieval tool") def retrieval_tool(question: str) -> str: return retrieval_tool(question) @humanloop.prompt(path="Evals demo/LLM call") def call_llm(**inputs): return call_llm(**inputs) @humanloop.flow(path="Evals demo/MedQA pipeline") def ask_question(**inputs): retrieved_data = retrieval_tool(inputs["question"]) inputs = {**inputs, "retrieved_data": retrieved_data} return call_llm(**inputs) ``` You can now run the pipeline as before and the full trace will be logged to Humanloop. ```python output = ask_question( **{ "question": "A 34-year-old male suffers from inherited hemophilia A. He and his wife have three unaffected daughters. What is the probability that the second daughter is a carrier of the disease?", "option_A": "0%", "option_B": "25%", "option_C": "50%", "option_D": "75%", "option_E": "100%", } ) print(output) ``` ## Re-run the Evaluation These decorated functions can similarly be used to run an Eval on the pipeline. This will allow you to evaluate the pipeline and see the detailed logs for each step in the pipeline. Let's change from `gpt-4o-mini` to `gpt-4o` and re-run the Eval. By passing in the same `name` to `humanloop.evaluations.run(...)` call, we'll add another run to the previously-created Evaluation on Humanloop. This will allow us to compare the two Runs side-by-side. ```python model = "gpt-4o" checks = humanloop.evaluations.run( name="RAG guide", file={ "path": "Evals demo/MedQA pipeline", "callable": ask_question_decorated, "type": "flow", }, dataset={ "path": "Evals demo/MedQA test", "datapoints": datapoints, }, evaluators=[ {"path": "Evals demo/Exact match"}, {"path": "Evals demo/Levenshtein"}, {"path": "Evals demo/Reasoning"}, {"path": "Example Evaluators/Code/Latency"}, ], ) ``` Viewing our Evaluation on Humanloop, we can see that our newly-added Run with `gpt-4o` has been added to the Evaluation. On the **Stats** tab, we can see that `gpt-4o` scores better for our "Exact match" (and "Levenshtein") metrics, but has higher latency.  Perhaps surprisingly, `gpt-4o` performs worse according to our "Reasoning" Evaluator. ## Logging with the API Above, we've let the SDK handle logging and versioning for us. However, you can also log data to Humanloop using the API directly. This can be useful if you want to perform some post-processing on the data before logging it, or if you want to include additional metadata in the logs or versions. We'll now demonstrate how to extend your Humanloop logging with more fidelity; creating Tool, Prompt, and Flow Logs to give you full visibility. We add additional logging steps to our `ask_question` function to represent the full trace of events on Humanloop. (Note that the `run_id` and `source_datapoint_id` arguments are optional, and are included here for use in the Evaluation workflow demonstrated later.) ```python from datetime import datetime import inspect def ask_question_with_logging(run_id: str | None = None, source_datapoint_id: str | None = None, **inputs)-> str: """Ask a question and get an answer using a simple RAG pipeline.""" trace = humanloop.flows.log( path="evals_demo/medqa-flow", flow={ "attributes": { "prompt": { "template": template, "model": model, "temperature": temperature, }, "tool": { "name": "retrieval_tool_v3", "description": "Retrieval tool for MedQA.", "source_code": inspect.getsource(retrieval_tool), }, } }, inputs=inputs, start_time=datetime.now(), run_id=run_id, source_datapoint_id=source_datapoint_id, ) # Retrieve context start_time=datetime.now() retrieved_data = retrieval_tool(inputs["question"]) inputs = {**inputs, "retrieved_data": retrieved_data} # Log the retriever information to Humanloop separately humanloop.tools.log( path="Evals demo/Retrieval tool", tool={ "function": { "name": "retrieval_tool", "description": "Retrieval tool for MedQA.", }, "source_code": inspect.getsource(retrieval_tool), }, output=retrieved_data, trace_parent_id=trace.id, start_time=start_time, end_time=datetime.now() ) # Populate the Prompt template start_time=datetime.now() messages = populate_template(template, inputs) # Call OpenAI to get response chat_completion= openai.chat.completions.create( model=model, temperature=temperature, messages=messages, ) output = chat_completion.choices[0].message.content # Log the prompt information to Humanloop separately humanloop.prompts.log( path="evals_demo/medqa-answer", prompt={ "model": model, "temperature": temperature, "template": template, }, inputs=inputs, output=output, output_message=chat_completion.choices[0].message, trace_parent_id=trace.id, start_time=start_time, end_time=datetime.now() ) # Close the trace humanloop.flows.update_log( log_id=trace.id, output=output, trace_status="complete", ) return output ``` The logging we've added here is similar to the SDK decorators we used earlier. ## Run an Evaluation using the API To orchestrate your own Evaluations, you can pass in `run_id` and `source_datapoint_id` to the `humanloop.flows.log(...)` call to associate Logs with a specific Run and Datapoint. The following is an example of how you can manually create an Evaluation and Run, and log data to Humanloop using the API, giving you full control over the Evaluation process. ```python from tqdm import tqdm # Create Evaluation evaluation = humanloop.evaluations.create( name="Manual logging demo", file={"path": "Evals demo/MedQA pipeline"}, evaluators=[ {"path": "Evals demo/Exact match"}, {"path": "Evals demo/Levenshtein"}, {"path": "Evals demo/Reasoning"}, {"path": "Example Evaluators/Code/Latency"}, ], ) # Create Run run = humanloop.evaluations.create_run(id=evaluation.id, dataset={"path": "Evals demo/MedQA test"}) # Run the pipeline over the Dataset for datapoint in tqdm(datapoints): ask_question_with_logging(run_id=run.id, source_datapoint_id=datapoint.id, **datapoint.inputs) ``` You can then similarly view results on the Humanloop UI.  This concludes the Humanloop RAG Evaluation walkthrough. You've learned how to integrate Humanloop into your RAG pipeline, set up logging, create Datasets, configure Evaluators, run Evaluations, and log the full trace of events including Tool and Prompt steps. # Evaluate an agent > Evaluate and improve the performance of an LLM agent. Working with LLMs is daunting: you are dealing with a black box that outputs unpredictable results. Humanloop provides tools to make your development process systematic, bringing it closer to traditional software testing and quality assurance. In this tutorial, we will use Humanloop to evaluate the quality of a chat agent's answers and demonstrate how to use results to improve the agent's performance. ## Prerequisites## Create the agent We will build an agent that answers questions asked by children. The agent queries Wikipedia and replies with an easy-to-understand explanation. Let's create the initial version of our agent. Add the following in a new file: Create a Humanloop Account
If you haven't already, [create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to HumanloopAdd an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.Using the Prompt Editor will use your OpenAI credits in the same way that the OpenAI playground does. Keep your API keys for Humanloop and the model providers private. Install the project's dependencies: ```python pip install humanloop openai wikipedia ``` Humanloop SDK requires Python 3.9 or higher. Optionally, create a virtual environment to keep dependencies tidy. {/* TODO: Add a disclaimer for TS */} Install the project's dependencies: ```typescript npm install humanloop openai wikipedia ``` ```python title="main.py" maxLines=35 from humanloop import Humanloop from openai import OpenAI from openai.types.chat.chat_completion_message import ChatCompletionMessage as Message import wikipedia import json openai = OpenAI(api_key="ADD YOUR KEY HERE") humanloop = Humanloop(api_key="ADD YOUR KEY HERE") def search_wikipedia(query: str) -> dict: """Search Wikipedia to get up-to-date information for a query.""" try: page = wikipedia.page(query) return { "title": page.title, "content": page.content, "url": page.url, } except Exception as _: return { "title": "", "content": "No results found", "url": "", } def call_model(messages: list[Message]) -> Message: """Calls the model with the given messages""" system_message = { "role": "system", "content": ( "You are an assistant that helps to answer user questions. " "You should leverage wikipedia to answer questions so that " "the information is up to date. If the response from " "Wikipedia does not seem relevant, rephrase the question " "and call the tool again. Then finally respond to the user." ), } response = openai.chat.completions.create( model="gpt-4o", messages=[system_message] + messages, tools=[ { "type": "function", "function": { "name": "search_wikipedia", "description": "Search the internet to get up to date answers for a query.", "parameters": { "type": "object", "required": ["query"], "properties": { "query": {"type": "string"}, }, "additionalProperties": False, }, }, } ], ) return response.choices[0].message.to_dict(exclude_unset=False) def call_agent(question: str) -> str: """Calls the main agent loop and returns the final result""" messages = [{"role": "user", "content": query}] # Retry for a relevant response 3 times at most for _ in range(3): response = call_model(messages) messages.append(response) if response["tool_calls"]: # Call wikipedia to get up-to-date information for tool_call in response["tool_calls"]: source = search_wikipedia( **json.loads(tool_call["function"]["arguments"]) ) messages.append( { "role": "tool", "content": json.dumps(source), "tool_call_id": tool_call["id"], } ) else: # Respond to the user return response["content"] if __name__ == "__main__": result = call_agent("Where does the sun go at night?") print(result) ``` ```typescript title="main.ts" maxLines=35 import { HumanloopClient } from "humanloop"; import OpenAI from "openai"; import type { ChatCompletionMessageParam as Message } from "openai/resources"; import wikipedia from "wikipedia"; import fs from "fs"; import readline from "readline"; const openai = new OpenAI({ apiKey: " Run the agent and check if it works:" }); const humanloop = new HumanloopClient({ apiKey: " " }); type WikiResult = { title: string; content: string; url: string; }; const searchWikipedia = async (query: string) => { try { const page = await wikipedia.page(query); const NO_RESULT_FOUND: WikiResult = { title: "", content: "No results found", url: "", }; if (page) { return { title: page?.title || "", content: (await page?.content()) || "", url: `https://en.wikipedia.org/wiki/${encodeURIComponent( page?.title || "" )}`, } as WikiResult; } return NO_RESULT_FOUND; } catch (error) { return NO_RESULT_FOUND; } }; const callModel = async (messages: Array ) => { const systemMessage: Message = { role: "system", content: "You are an assistant that helps to answer user questions. " + "You should leverage wikipedia to answer questions so that " + "the information is up to date. If the response from " + "Wikipedia does not seem relevant, rephrase the question " + "and call the tool again. Then finally respond to the user.", }; const response = await openai.chat.completions.create({ model: "gpt-4o", messages: [systemMessage, ...messages], tools: [ { type: "function", function: { name: "search_wikipedia", description: "Search the internet to get up to date answers for a query.", parameters: { type: "object", required: ["query"], properties: { query: { type: "string" }, }, additionalProperties: false, }, }, }, ], }); return response.choices[0].message; } async function callAgent({ question }: { question: string }): Promise { const messages: Message[] = [{ role: "user", content: question }]; for (let _ = 0; _ < 3; _++) { const response = await callModel(messages); messages.push(response); if (response.tool_calls) { for (const toolCall of response.tool_calls) { const args = JSON.parse(toolCall.function.arguments); const source = await searchWikipedia(args.query); messages.push({ role: "tool", content: JSON.stringify(source), tool_call_id: toolCall.id, }); } } else { return response.content || ""; } } return "Could not get a relevant response after multiple attempts."; } async function main() { const result = await callAgent({ question: "Where does the sun go at night?", }); console.log(result); } main(); ``` ## Evaluate the agent ```bash python main.py ``` ```plaintext Okay! Imagine the Earth is like a big ball, and we live on it. The sun doesn't really “go” anywhere—it stays in the same spot, shining all the time. But our Earth is spinning like a top! ``` ```bash npx tsx main.ts ``` ```plaintext Okay! Imagine the Earth is like a big ball, and we live on it. The sun doesn't really “go” anywhere—it stays in the same spot, shining all the time. But our Earth is spinning like a top! ``` Evaluators are callables that take the Log's dictionary representation as input and return a judgment. The Evaluator's judgment should respect the `return_type` present in Evaluator's [specification](https://humanloop.com/docs/v5/api-reference/evaluators/upsert#request.body.spec). The Evaluator can take an additional `target` argument to compare the Log against. The target is provided in an Evaluation context by the validation [Dataset](/docs/v5/explanation/datasets). For more details, check out our [Evaluator explanation](/docs/v5/explanation/evaluators). Let's check if the agent respects the requirement of providing easy-to-understand answers. We will create an [Evaluation](/docs/v5/guides/evals/run-evaluation-ui) to benchmark the performance of the agent. An Evaluation requires a [Dataset](/docs/v5/guides/explanations/datasets) and at least one [Evaluator](/docs/v5/guides/explanations/evaluators).### Create LLM judge We will use an LLM judge to automatically evaluate the agent's responses. We will define the Evaluator in code, but you can also [manage Evaluators in the UI](/docs/v5/guides/evals/llm-as-a-judge). Add this to your `main` function: ## Iterate and evaluate again The score of the initial setup is quite low. Click the Evaluation link from the terminal and switch to the Logs view. You will see that the model tends to provide elaborate answers.```python title="main.py" if __name__ == "__main__": humanloop.evaluators.upsert( path="QA Agent/Comprehension", spec={ "arguments_type": "target_free", "return_type": "number", "evaluator_type": "llm", "prompt": { "model": "gpt-4o", "endpoint": "complete", "template": ( "You must decide if an explanation is simple " "enough to be understood by a 5-year old. " "A better explanation is shorter and uses less jargon. " "Rate the answer from 1 to 10, where 10 is the best.\n" "\n ### Add Dataset Create a file called `dataset.jsonl` and add the following: ```jsonl title="dataset.jsonl" maxLines=5 {"inputs": {"question": "Why is the sky blue?"}} {"inputs": {"question": "Where does the sun go at night?"}} {"inputs": {"question": "Why do birds fly?"}} {"inputs": {"question": "What makes rainbows?"}} {"inputs": {"question": "Why do we have to sleep?"}} {"inputs": {"question": "How do fish breathe underwater?"}} {"inputs": {"question": "Why do plants need water?"}} {"inputs": {"question": "How does the moon stay in the sky?"}} {"inputs": {"question": "What are stars made of?"}} {"inputs": {"question": "Why do we have seasons?"}} {"inputs": {"question": "How does the TV work?"}} {"inputs": {"question": "Why do dogs wag their tails?"}} {"inputs": {"question": "What makes cars go?"}} {"inputs": {"question": "Why do we need to brush our teeth?"}} {"inputs": {"question": "What do ants eat?"}} {"inputs": {"question": "Why does the wind blow?"}} {"inputs": {"question": "How do airplanes stay in the air?"}} {"inputs": {"question": "Why does the ocean look so big?"}} {"inputs": {"question": "What makes the grass green?"}} {"inputs": {"question": "Why do we have to eat vegetables?"}} {"inputs": {"question": "How do butterflies fly?"}} {"inputs": {"question": "Why do some animals live in the zoo?"}} {"inputs": {"question": "How do magnets stick to the fridge?"}} {"inputs": {"question": "What makes fire hot?"}} {"inputs": {"question": "Why do leaves change color?"}} {"inputs": {"question": "What happens when we flush the toilet?"}} {"inputs": {"question": "Why do we have belly buttons?"}} {"inputs": {"question": "What makes the clouds move?"}} {"inputs": {"question": "Why do we have eyebrows?"}} {"inputs": {"question": "How do seeds turn into plants?"}} {"inputs": {"question": "Why does the moon change shape?"}} {"inputs": {"question": "Why do bees make honey?"}} {"inputs": {"question": "What makes ice melt?"}} {"inputs": {"question": "Why do we sneeze?"}} {"inputs": {"question": "How do trains stay on the tracks?"}} {"inputs": {"question": "Why do stars twinkle?"}} {"inputs": {"question": "Why can't we see air?"}} {"inputs": {"question": "What makes the Earth spin?"}} {"inputs": {"question": "Why do frogs jump?"}} {"inputs": {"question": "Why do cats purr?"}} {"inputs": {"question": "How do phones let us talk to people far away?"}} {"inputs": {"question": "Why does the moon follow us?"}} {"inputs": {"question": "What makes lightning?"}} {"inputs": {"question": "Why does it snow?"}} {"inputs": {"question": "Why do we have shadows?"}} {"inputs": {"question": "Why do boats float?"}} {"inputs": {"question": "What makes our heart beat?"}} {"inputs": {"question": "Why do some animals sleep all winter?"}} {"inputs": {"question": "Why do we have to wear shoes?"}} {"inputs": {"question": "What makes music?"}} ``` ### Run an Evaluation Add this to your `main` function:\n{{log.inputs.question}}\n \n\n" "\n\n{{log.output}} \n\n" "First provide your rationale, then on a newline, " "output your judgment." ), "provider": "openai", "temperature": 0, }, }, ) ``` ```typescript title="main.ts" async function main() { await humanloop.evaluators.upsert({ path: "QA Agent/Comprehension", spec: { argumentsType: "target_free", returnType: "number", evaluatorType: "llm", prompt: { model: "gpt-4o", endpoint: "complete", template: "You must decide if an explanation is simple " + "enough to be understood by a 5-year old. " + "A better explanation is shorter and uses less jargon. " + "Rate the answer from 1 to 10, where 10 is the best.\n" + "\n\n{{log.inputs.question}}\n \n\n" + "\n\n{{log.output}} \n\n" + "First provide your rationale, then on a newline, " + "output your judgment.", provider: "openai", temperature: 0, }, }, }); ``````python title="main.py" maxLines=100 highlight={4-24} if __name__ == "__main__": # ... # Read the evaluation dataset with open("dataset.jsonl", "r") as fp: dataset = [json.loads(line) for line in fp] humanloop.evaluations.run( name="QA Agent Answer Check", file={ "path": "QA Agent/Agent", "callable": call_agent, }, evaluators=[{"path": "QA Agent/Comprehension"}], dataset={ "path": "QA Agent/Dataset", "datapoints": dataset, }, workers=8, ) ``` ```typescript title="main.ts" maxLines=100 highlight={4-29} async function main() { // ... // Read the evaluation dataset const dataset: any[] = []; const fileStream = fs.createReadStream("dataset.jsonl"); const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity, }); for await (const line of rl) { dataset.push(JSON.parse(line)); } // Run the evaluation await humanloop.evaluations.run({ name: "QA Agent Answer Check", file: { path: "QA Agent/Agent", callable: callAgent, }, evaluators: [{ path: "QA Agent/Comprehension" }], dataset: { path: "QA Agent/Comprehension", datapoints: dataset, }, concurrency: 8, }); } ``` Run your file and let the Evaluation finish:```bash title="Terminal" python main.py ``` ```bash title="Terminal" maxLines=50 Navigate to your Evaluation: https://app.humanloop.com/project/fl_9CCIoTzySPfUFeIxfYE6g/evaluations/evr_67tEc2DiR83fy9iTaqyPA/stats Flow Version ID: flv_9ECTrfeZYno2OIj9KAqlz Run ID: rn_67tEcDYV6mqUS86hD8vrP Running 'Agent' over the Dataset 'Children Questions' using 8 workers [##############--------------------------] 15/50 (30.00%) | ETA: 14 ... 📊 Evaluation Results for QA Agent/Agent +------------------------+---------------------+ | | Latest | +------------------------+---------------------+ | Run ID | 67tEc | +------------------------+---------------------+ | Version ID | 9ECTr | +------------------------+---------------------+ | Added | 2024-11-19 21:49:02 | +------------------------+---------------------+ | Evaluators | | +------------------------+---------------------+ | QA Agent/Comprehension | 3.24 | +------------------------+---------------------+ ``` ```bash title="Terminal" maxLines=50 npx tsx main.ts ``` ```bash title="Terminal" maxLines=50 Navigate to your Evaluation: https://app.humanloop.com/project/fl_9CCIoTzySPfUFeIxfYE6g/evaluations/evr_67tEc2DiR83fy9iTaqyPA/stats Flow Version ID: flv_9ECTrfeZYno2OIj9KAqlz Run ID: rn_67tEcDYV6mqUS86hD8vrP Running 'Agent' over the Dataset 'Children Questions' using 8 workers [##############--------------------------] 15/50 (30.00%) | ETA: 14 ... 📊 Evaluation Results for QA Agent/Agent +------------------------+---------------------+ | | Latest | +------------------------+---------------------+ | Run ID | 67tEc | +------------------------+---------------------+ | Version ID | 9ECTr | +------------------------+---------------------+ | Added | 2024-11-19 21:49:02 | +------------------------+---------------------+ | Evaluators | | +------------------------+---------------------+ | QA Agent/Comprehension | 3.24 | +------------------------+---------------------+ ``` Let's modify the LLM prompt inside `call_model`:
```python title="main.py" maxLines=100 highlight={11-12} def call_model(messages: list[Message]) -> Message: """Calls the model with the given messages""" system_message = { "role": "system", "content": ( "You are an assistant that help to answer user questions. " "You should leverage wikipedia to answer questions so that " "the information is up to date. If the response from Wikipedia " "does not seem relevant, rephrase the question and call the " "tool again. Then finally respond to the user. " "Formulate the response so that it is easy to understand " "for a 5 year old." ) } response = openai.chat.completions.create( model="gpt-4o", messages=[system_message] + messages, tools=[ { "type": "function", "function": { "name": "search_wikipedia", "description": "Search the internet to get up to date answers for a query.", "parameters": { "type": "object", "required": ["query"], "properties": { "query": {"type": "string"}, }, "additionalProperties": False, }, } } ], ) return response.choices[0].message.to_dict(exclude_unset=False) ``` ```typescript title="main.ts" maxLines=100 highlight={10-11} const callModel = async (messages: Array Run the agent again and let the Evaluation finish:) => { const systemMessage: Message = { role: "system", content: "You are an assistant that helps to answer user questions. " + "You should leverage wikipedia to answer questions so that " + "the information is up to date. If the response from " + "Wikipedia does not seem relevant, rephrase the question " + "and call the tool again. Then finally respond to the user. "+ "Formulate the response so that it is easy to understand " + "for a 5 year old.", }; const response = await openai.chat.completions.create({ model: "gpt-4o", messages: [systemMessage, ...messages], tools: [ { type: "function", function: { name: "search_wikipedia", description: "Search the internet to get up to date answers for a query.", parameters: { type: "object", required: ["query"], properties: { query: { type: "string" }, }, additionalProperties: false, }, }, }, ], }); return response.choices[0].message; } ``` ```python python main.py ``` ```typescript npx tsx main.ts ``` ```bash title="Terminal" maxLines=50 Flow Version ID: flv_9ECTrfeZYno2OIj9KAqlz Run ID: rn_WnIwPSI7JFKEtwTS0l3mj Navigate to your Evaluation: https://app.humanloop.com/project/fl_9CCIoTzySPfUFeIxfYE6g/evaluations/rn_WnIwPSI7JFKEtwTS0l3mj/stats Running 'Agent' over the Dataset 'Children Questions' using 8 workers [######################------------------] 34/50 (68.00%) | ETA: 14 ... +------------------------+---------------------+---------------------+ | | Control | Latest | +------------------------+---------------------+---------------------+ | Run ID | 67tEc | WnIwP | +------------------------+---------------------+---------------------+ | Version ID | 9ECTr | 9ECTr | +------------------------+---------------------+---------------------+ | Added | 2024-11-19 22:05:17 | 2024-11-19 22:24:13 | +------------------------+---------------------+---------------------+ | Evaluators | | | +------------------------+---------------------+---------------------+ | QA Agent/Comprehension | 3.24 | 8.04 | +------------------------+---------------------+---------------------+ Change of [4.80] for Evaluator QA Agent/Comprehension ``` Click the Evaluation link again. The agent's performance has improved significantly.## Add detailed logging
If you use a programming language not supported by the SDK, or want more control, see our guide on [logging through the API](/docs/v5/guides/observability/logging-through-api) for an alternative to decorators. Up to this point, we have treated the agent as a black box, reasoning about its behavior by looking at the inputs and outputs. Let's use Humanloop logging to observe the step-by-step actions taken by the agent.Evaluate the agent again. When it's done, head to your workspace and click the **Agent** [Flow](/docs/v5/guides/explanations/flows) on the left. Select the Logs tab from the top of the page. Modify `main.py`: ```python title="main.py" maxLines=100 highlight={1,5,10,15} @humanloop.tool(path="QA Agent/Search Wikipedia") def search_wikipedia(query: str) -> dict: ... @humanloop.prompt(path="QA Agent/Prompt") def call_model(messages: list[Message]) -> Message: ... @humanloop.flow(path="QA Agent/Agent") def call_agent(question: str) -> str: ... ``` To auto-instrument calls to OpenAI, pass the module in the Humanloop constructor: ```typescript const humanloop = new HumanloopClient({ apiKey: process.env.HUMANLOOP_API_KEY, providers: { // Pass the OpenAI module, not the initialized client OpenAI } }); ``` Modify `main.ts`: ```typescript title="main.ts" maxLines=100 highlight={20-21,26-27,32-33} const searchWikipedia = humanloop.tool({ path: "QA Agent/Search Wikipedia", version: { function: { name: "Search Wikipedia", description: "Search Wikipedia for the best article to answer a question", strict: true, parameters: { type: "object", properties: { query: { type: "string", description: "The question to search Wikipedia for", }, }, required: ["query"], }, }, }, // Wraps the initial function body callable: async ({ query }) => { ... }, }); const callModel = humanloop.prompt({ path: "QA Agent/Prompt", // Wraps the initial function body callable: async ({ messages }) => { ... }, }); const callAgent = humanloop.flow({ path: "QA Agent/Agent", // Wraps the initial function body callable: async ({ question }) => { ... }, }); ```The decorators divide the code in logical components, allowing you to observe the steps taken to answer a question. Every step taken by the agent creates a Log. ## Next steps We've built a complex agentic workflow and learned how to use Humanloop to add logging to it and evaluate its performance. Take a look at these resources to learn more about evals on Humanloop: * Learn how to [create a custom dataset](/docs/v5/guides/evals/create-dataset) for your project. * Learn more about using [LLM Evaluators](/docs/v5/guides/evals/llm-as-a-judge) on Humanloop. # Capture user feedback > Collect feedback from your users to improve your AI product. In this tutorial, we'll show how you can gather valuable insights from your users to evaluate and improve your AI product. We'll deploy a simple chat app that allows users to interact with an AI model. Later, we'll modify the source code to capture user feedback and show how these insights are used to improve the AI product. ### Prerequisites
## Capture user feedback You can grab the source code used in this tutorial here: [hl-chatgpt-clone-typescript](https://github.com/humanloop/hl-chatgpt-clone-typescript) Create a Humanloop Account
If you haven't already, [create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to HumanloopAdd an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.Using the Prompt Editor will use your OpenAI credits in the same way that the OpenAI playground does. Keep your API keys for Humanloop and the model providers private. ### Clone and start a chat app server ```bash git clone https://github.com/humanloop/hl-chatgpt-clone-typescript # add Humanloop API key touch .env.local echo HUMANLOOP_API_KEY=YOUR_API_KEY >> .env.local # optionally add OpenAI key, if you haven't already in Humanloop app echo OPENAI_API_KEY=YOUR_API_KEY >> .env.local # run the app bun install bun run dev ``` ### Use the chat app Open the chat app in your browser and start chatting with the AI model. ## Use the logs to improve your AI product After you collect enough data, you can leverage the user feedback to improve your AI product. Navigate back to the Logs view and filter all Logs that have a 'bad' rating to review the model's responses that need improvement.Every time the user presses the Send button, Humanloop receives the request and calls the AI model. The response from the model is then stored as a Log. Let's check the `api/chat/route.ts` file to see how it works. * The `path` parameter is the path to the Prompt in the Humanloop workspace. If the Prompt doesn't exist, it will be created. * The `prompt` parameter is the configuration of the Prompt. In this case we manage our Prompt in code; if the configuration of the Prompt changes, a new version of the Prompt will automatically be created on Humanloop. Prompts can alternatively be managed [directly on Humanloop](/docs/development/guides/call-prompt). * The `messages` parameter is the list of all messages exchanged between the Model and the User. To learn more about calling Prompts with the Humanloop SDK, see the [Prompt Call](/v5/api-reference/prompts/call-stream) API reference. ```typescript api/chat/route.ts const response = await humanloop.prompts.callStream({ // if Prompt doesn't exist, it will be created path: "chatgpt-clone-tutorial/customer-support-agent", prompt: { model: "gpt-4", template: [ { role: "system", content: "You are a helpful assistant.", }, ], }, // messages is a list of objects: [{role: string, content: string}, ...]. // Role is either "user", "assistant", "system", or "tool". messages, providerApiKeys: { // OpenAI API key, if you haven't already set it in Humanloop app openai: process.env.OPENAI_API_KEY, }, }); ``` ### Review the logs in Humanloop After chatting with the AI model, go to the Humanloop app and review the logs. Click on the `chatgpt-clone-tutorial/customer-support-agent` Prompt, then click on the Logs tab at the top of the page. You see that all the interactions with the AI model are logged here.
The code will generate a new Prompt `chatgpt-clone-tutorial/customer-support-agent` in the Humanloop app. To change the path, modify the variable `PROMPT_HUMANLOOP_PATH` in the `api/chat/route.ts` file. ### Modify the code to capture user feedback Now, let's modify the code to start getting user feedback! Go back to the code editor and uncomment lines 174-193 in the `page.tsx` file. This snippet will add 👍 and 👎 buttons, that users can press to give feedback on the model's responses. ```typescript highlight={13-31} return (
); ``` To understand how the feedback is captured and sent to Humanloop, let's check the `api/feedback/route.ts` file. We use [Humanloop TypeScript SDK](https://www.npmjs.com/package/humanloop) to make calls to Humanloop. To attach user feedback, we only need three parameters: * `parentId` is the Id of the [Log](../../explanation/logs) to which we want to attach feedback. The `page.txs` file stores all log Ids for model responses. * `path` is the path to the Evaluator. In this example, we're using an example 'rating' Evaluator. * `judgment` is the user feedback. ```typescript api/feedback/route.ts const response = await humanloop.evaluators.log({ // Pass the `logId` of the Prompt Log to record feedback against. parentId: logId, // Here, we're recording feedback against an example "rating" Evaluator, // which is of type `select` and has two possible options: "good" and "bad." path: "Example Evaluators/Human/rating", // Alternatively, we advise to specify Evaluator by id. This is more robust and less error-prone. // versionId: "ev_9WiSw2VYWjAb22duuQ"; judgment: judgment, //user feedback }); ``` ### Capture user feedback Refresh the page in your browser and give 👍 or 👎 to the model's responses.{message.role}{message.content ? ({message.content as string}) : (...)} {logId && ()}![]()
In the tutorial, we used the 'rating' Evaluator to capture user feedback. However, different use cases and user interfaces may require various types of feedback that need to be mapped to the appropriate end-user interactions. There are broadly 3 important kinds of feedback: 1. **Explicit feedback**: these are purposeful actions to review the generations. For example, ‘thumbs up/down’ button presses. 2. **Implicit feedback**: indirect actions taken by your users may signal whether the generation was good or bad, for example, whether the user ‘copied’ the generation, ‘saved it’ or ‘dismissed it’ (which is negative feedback). 3. **Free-form feedback**: Corrections and explanations provided by the end-user on the generation. You should create Human Evaluators structured to capture the feedback you need. For example, a Human Evaluator with return type "text" can be used to capture free-form feedback, while a Human Evaluator with return type "multi\_select" can be used to capture user actions that provide implicit feedback. If you have not done so, you can follow our guide to [create a Human Evaluator](/docs/evaluation/guides/human-evaluator) to set up the appropriate feedback schema. ### Review the logs in Humanloop With the user feedback captured, go back to the Humanloop app and review the logs. On the Performance tab, you can see all Evaluators and their values. The user feedback is captured in the rating Evaluator ('good' for 👍 and 'bad' for 👎).![]()
Click on Log and then on **Editor ->** button in the top right corner to open the Prompt Editor. In the Prompt Editor, you can make changes to the instructions and the model's parameters to improve the model's performance. Once you're happy with the changes, deploy the new version of the Prompt.
When users start interacting with the new version, compare the "good" to "bad" ratio to see if the changes have improved your users' experience. ## Next steps Now that you've successfully captured user feedback, you can explore more ways to improve your AI product: * If you found that your Prompt doesn't perform well, see our guide on [Comparing and Debugging Prompts](/docs/evaluation/guides/comparing-prompt-editor). * Leverage [Code](/docs/evaluation/guides/code-based-evaluator), [AI](/docs/evaluation/guides/llm-as-a-judge) and [Human](/docs/evaluation/guides/human-evaluators) Evaluators to continuously monitor and improve your AI product. # Run an Evaluation via the UI > How to use Humanloop to evaluate multiple different Prompts across a Dataset. An **Evaluation** on Humanloop leverages a [Dataset](../../explanation/datasets), a set of [Evaluators](../../explanation/evaluators) and different versions of a [Prompt](../../explanation/prompts) to compare. The Dataset contains datapoints describing the inputs (and optionally the expected results) for a given task. The Evaluators define the criteria for judging the performance of the Prompts when executed using these inputs. [comment]: <> "The above should be in Explanation section but we haven't move it there" Prompts, when evaluated, produce [Logs](../../explanation/logs). These Logs are then judged by the Evaluators. You can see the summary of Evaluators judgments to systematically compare the performance of the different Prompt versions. ### Prerequisites * A set of [Prompt](../../explanation/prompts) versions you want to compare - see the guide on [creating Prompts](./comparing-prompt-editor). * A [Dataset](../../explanation/datasets) containing datapoints for the task - see the guide on [creating a Dataset](./create-dataset). * At least one [Evaluator](../../explanation/evaluators) to judge the performance of the Prompts - see the guides on creating [Code](/docs/evaluation/guides/code-based-evaluator), [AI](/docs/evaluation/guides/llm-as-a-judge) and [Human](/docs/evaluation/guides/human-evaluators) Evaluators. ## Run an Evaluation via UI For this example, we're going to evaluate the performance of a Support Agent that responds to user queries about Humanloop's product and documentation. Our goal is to understand which base model between `gpt-4o`, `gpt-4o-mini` and `claude-3-5-sonnet-20241022` is most appropriate for this task.
### Navigate to the Evaluations tab of your Prompt * Go to the Prompt you want to evaluate and then click on **Evaluations** tab at the top of the page. * Click the **Evaluate** button top right to create a new Evaluation. * Click the **+Run** button top right to create a new Evaluation Run. ### Next Steps * Incorporate this Evaluation process into your Prompt engineering and deployment workflow. * Setup Evaluations where the runtime for producing Logs lives in your code - see our guide on [Logging](/docs/development/guides/log-to-a-prompt). * Utilise Evaluations as part of your [CI/CD pipeline](/docs/evaluation/guides/cicd-integration) # Run an Evaluation via the API > In this guide, we will walk through how to programmatically evaluate multiple different Prompts to compare the quality and performance of each version. An **Evaluation** on Humanloop leverages a [Dataset](../../explanation/datasets), a set of [Evaluators](../../explanation/evaluators) and different versions of a [Prompt](../../explanation/prompts) to compare.### Set up an Evaluation Run * Select a Dataset using **+Dataset**. * Add the Prompt versions you want to compare using **+Prompt**. * Add the Evaluators you want to use to judge the performance of the Prompts using **+Evaluator**.
By default the system will re-use Logs if they exist for the chosen Dataset, Prompts and Evaluators. This makes it easy to extend Evaluation Run without paying the cost of re-running your Prompts and Evaluators. If you want to force the system to re-run the Prompts against the Dataset producing a new batch of Logs, you can click on regenerate button next to the Logs count * Click **Save**. Humanloop will start generating Logs for the Evaluation.![]()
This guide assumes both the Prompt and Evaluator Logs are generated using the Humanloop runtime. For certain use cases where more flexibility is required, the runtime for producing Logs instead lives in your code - see our guide on [Logging](../../development/guides/logging), which also works with our Evaluations feature. We have a guide for how to run Evaluations with Logs generated in your code coming soon! ### Review the results Once the Logs are produced, you can review the performance of the different Prompt versions by navigating to the **Stats** tab. * The top spider plot provides you with a summary of the average Evaluator performance across all the Prompt versions. In our case, `gpt-4o`, although on average slightly slower and more expensive on average, is significantly better when it comes to **User Satisfaction**.* Below the spider plot, you can see the breakdown of performance per Evaluator.
* To drill into and debug the Logs that were generated, navigate to the **Review** tab at top left of the Run page. The Review view allows you to better understand performance and replay logs in our Prompt Editor.
![]()
In this guide, we use a Dataset to evaluate the performance of different Prompt versions. To learn how to evaluate Prompts without a Dataset, see the guide on [Spot-check your Logs](./spot-check-logs). ### Prerequisites * A set of [Prompt](../../explanation/prompts) versions you want to compare - see the guide on [creating Prompts](./comparing-prompt-editor). * A [Dataset](../../explanation/datasets) containing datapoints for the task - see the guide on [creating a Dataset via API](./create-dataset-via-api). * At least one [Evaluator](../../explanation/evaluators) to judge the performance of the Prompts - see the guides on creating [Code](/docs/evaluation/guides/code-based-evaluator), [AI](/docs/evaluation/guides/llm-as-a-judge) and [Human](/docs/evaluation/guides/human-evaluators) Evaluators. ## Run an Evaluation For this guide, we're going to evaluate the performance of a Support Agent that responds to user queries about Humanloop's product and documentation. Our goal is to understand which base model between `gpt-4o`, `gpt-4o-mini` and `claude-3-5-sonnet-20241022` is most appropriate for this task.### Create a Prompt Create a Support Agent Prompt with three versions each using a different base model. # Run an Evaluation using your runtime If you choose to execute Prompts using your own Runtime, you still can benefit from Humanloop Evaluations. In code snippet below, we run Evaluators hosted on Humanloop using logs produced by the OpenAI client. ```python Python # create new Humanloop prompt prompt = humanloop.prompts.upsert( path="Run Evaluation via API/Support Agent my own runtime", model="gpt-4o", endpoint="chat", template=[ { "content": "You are a helpful assistant. Your job is to respond to FAQ style queries about the Humanloop documentation and platform. Be polite and succinct.", "role": "system", } ], provider="openai", ) # create the evaluation evaluation = humanloop.evaluations.create( name="Evaluation via API using my own runtime", file={ "path": "Run Evaluation via API/Support Agent my own runtime", }, evaluators=[{"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Cost"}, {"path": "Example Evaluators/Code/Latency"}], ) # use dataset created in previous steps datapoints = humanloop.datasets.list_datapoints(dataset.id) import openai openai_client = openai.OpenAI(api_key="USE_YOUR_OPENAI_API_KEY") # create a run run = humanloop.evaluations.create_run( id=evaluation.id, dataset={"version_id": dataset.version_id}, version={"version_id": prompt.version_id}, ) # for each datapoint in the dataset, create a chat completion for datapoint in datapoints: # create a run chat_completion = openai_client.chat.completions.create( messages=datapoint.messages, model=prompt.model ) # log the prompt humanloop.prompts.log( id=prompt.id, run_id=run.id, version_id=prompt.version_id, source_datapoint_id=datapoint.id, output_message=chat_completion.choices[0].message, messages=datapoint.messages, ) ``` ## Next steps * Learn how to [set up LLM as a Judge](./llm-as-a-judge) to evaluate your AI applications. # Upload a Dataset from CSV > Learn how to create Datasets in Humanloop to define fixed examples for your projects, and build up a collection of input-output pairs for evaluation and fine-tuning. [Datasets](../../explanation/datasets) are a collection of input-output pairs that can be used to evaluate your Prompts, Tools or even Evaluators. ### Prerequisites You should have an existing [Prompt](../../explanation/prompts) on Humanloop with a variable defined with our double curly bracket syntax `{{variable}}`. If not, first follow our guide on [creating a Prompt](../prompts/create-prompt). In this example, we'll use a Prompt that categorises user queries about Humanloop's product and docs by which feature they relate to.### Create a Dataset We defined sample data that contains user messages and desired responses for the Support Agent Prompt. We will now create a Dataset with these datapoints. ```python Python from humanloop import Humanloop humanloop = Humanloop(api_key="YOUR_API_KEY") system_message = "You are a helpful assistant. Your job is to respond to FAQ style queries about the Humanloop documentation and platform. Be polite and succinct." gpt_4o = humanloop.prompts.upsert( path="Run Evaluation via API/Support Agent", model="gpt-4o", endpoint="chat", template=[ { "content": system_message, "role": "system", } ], provider="openai", commit_message="gpt-4o", ) gpt_4o_mini = humanloop.prompts.upsert( path="Run Evaluation via API/Support Agent", model="gpt-4o-mini", endpoint="chat", template=[ { "content": system_message, "role": "system", } ], provider="openai", commit_message="gpt-4o-mini", ) sonnet = humanloop.prompts.upsert( path="Run Evaluation via API/Support Agent", model="claude-3-5-sonnet-20241022", endpoint="chat", template=[ { "content": system_message, "role": "system", } ], provider="anthropic", commit_message="claude-3-5-sonnet-20241022", ) # store prompt versions for later use prompt_versions = [gpt_4o.version_id, gpt_4o_mini.version_id, sonnet.version_id] ``` ```typescript TypeScript import { HumanloopClient } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); const systemMessage = "You are a helpful assistant. Your job is to respond to FAQ style queries about the Humanloop documentation and platform. Be polite and succinct."; const gpt_4o = await humanloop.prompts.upsert({ path: "Run Evaluation via API/Support Agent", model: "gpt-4o", endpoint: "chat", template: [ { "content": systemMessage, "role": "system", } ], provider:"openai", commitMessage: "gpt-4o", }) const gpt_4o_mini = await humanloop.prompts.upsert({ path: "Run Evaluation via API/Support Agent", model: "gpt-4o-mini", endpoint: "chat", template: [ { "content": systemMessage, "role": "system", } ], provider:"openai", commitMessage: "gpt-4o-mini", }) const sonnet = await humanloop.prompts.upsert({ path: "Run Evaluation via API/Support Agent", model: "claude-3-5-sonnet-20241022", endpoint: "chat", template: [ { "content": systemMessage, "role": "system", } ], provider:"anthropic", commitMessage: "claude-3-5-sonnet-20241022", }) // store prompt versions for later use const promptVersions = [gpt_4o.versionId, gpt_4o_mini.versionId, sonnet.versionId] ``` ### Create an Evaluation We create an Evaluation Run to compare the performance of the different Prompts using the Dataset we just created. For this guide, we selected *Semantic similarity*, *Cost* and *Latency* Evaluators. You can find these Evaluators in the **Example Evaluators** folder in your workspace. ```python Python humanloop.datasets.upsert( path="Run Evaluation via API/Dataset with user questions", datapoints=[ { "messages": [{ "role": "user", "content": "How do i manage my organizations API keys?", }], "target": {"answer": "Hey, thanks for your questions. Here are steps for how to achieve: 1. Log in to the Humanloop Dashboard \n\n2. Click on \"Organization Settings.\"\n If you do not see this option, you might need to contact your organization admin to gain the necessary permissions.\n\n3. Within the settings or organization settings, select the option labeled \"API Keys\" on the left. Here you will be able to view and manage your API keys.\n\n4. You will see a list of existing API keys. You can perform various actions, such as:\n - **Generate New API Key:** Click on the \"Generate New Key\" button if you need a new API key.\n - **Revoke an API Key:** If you need to disable an existing key, find the key in the list and click the \"Revoke\" or \"Delete\" button.\n - **Copy an API Key:** If you need to use an existing key, you can copy it to your clipboard by clicking the \"Copy\" button next to the key.\n\n5. **Save and Secure API Keys:** Make sure to securely store any new or existing API keys you are using. Treat them like passwords and do not share them publicly.\n\nIf you encounter any issues or need further assistance, it might be helpful to engage with an engineer or your IT department to ensure you have the necessary permissions and support.\n\nWould you need help with anything else?"}, }, { "messages":[{ "role": "user", "content": "Hey, can do I use my code evaluator for monitoring my legal-copilot prompt?", }], "target": {"answer": "Hey, thanks for your questions. Here are steps for how to achieve: 1. Navigate to your Prompt dashboard. \n 2. Select the `Monitoring` button on the top right of the Prompt dashboard \n 3. Within the model select the Version of the Evaluator you want to turn on for monitoring. \n\nWould you need help with anything else?"}, }, ], action="set", commit_message="Add two new questions and answers", ) ``` ```typescript TypeScript await humanloop.datasets.upsert({ path: "Run Evaluation via API/Dataset with user questions", datapoints: [{ "messages": [{ "role": "user", "content": "How do i manage my organizations API keys?", }], "target": {"answer": "Hey, thanks for your questions. Here are steps for how to achieve: 1. Log in to the Humanloop Dashboard \n\n2. Click on \"Organization Settings.\"\n If you do not see this option, you might need to contact your organization admin to gain the necessary permissions.\n\n3. Within the settings or organization settings, select the option labeled \"API Keys\" on the left. Here you will be able to view and manage your API keys.\n\n4. You will see a list of existing API keys. You can perform various actions, such as:\n - **Generate New API Key:** Click on the \"Generate New Key\" button if you need a new API key.\n - **Revoke an API Key:** If you need to disable an existing key, find the key in the list and click the \"Revoke\" or \"Delete\" button.\n - **Copy an API Key:** If you need to use an existing key, you can copy it to your clipboard by clicking the \"Copy\" button next to the key.\n\n5. **Save and Secure API Keys:** Make sure to securely store any new or existing API keys you are using. Treat them like passwords and do not share them publicly.\n\nIf you encounter any issues or need further assistance, it might be helpful to engage with an engineer or your IT department to ensure you have the necessary permissions and support.\n\nWould you need help with anything else?"}, }, { "messages":[{ "role": "user", "content": "Hey, can do I use my code evaluator for monitoring my legal-copilot prompt?", }], "target": {"answer": "Hey, thanks for your questions. Here are steps for how to achieve: 1. Navigate to your Prompt dashboard. \n 2. Select the `Monitoring` button on the top right of the Prompt dashboard \n 3. Within the model select the Version of the Evaluator you want to turn on for monitoring. \n\nWould you need help with anything else?"}, }], action: "set", commitMessage: "Add two new questions and answers" }); ``` "Semantic similarity" Evaluator measures the degree of similarity between the model's response and the expected output. The similarity is rated on a scale from 1 to 5, where 5 means very similar. ### Inspect the Evaluation stats When Runs are completed, you can inspect the Evaluation Stats to see the summary of the Evaluators judgments. ```python Python evaluation = humanloop.evaluations.create( name="Evaluation via API", file={ "path": "Run Evaluation via API/Support Agent", }, evaluators=[{"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Cost"}, {"path": "Example Evaluators/Code/Latency"}], ) # Create a Run for each prompt version for prompt_version in prompt_versions: humanloop.evaluations.create_run( id=evaluation.id, dataset={"path": "Run Evaluation via API/Dataset with user questions"}, version={"version_id": prompt_version}, ) ``` ```typescript TypeScript const evaluation = await humanloop.evaluations.create({ name: "Evaluation via API", file: { "path": "Run Evaluation via API/Support Agent", }, evaluators: [{"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Cost"}, {"path": "Example Evaluators/Code/Latency"}], }); for (const promptVersion of promptVersions) { await humanloop.evaluations.createRun(evaluation.id, { dataset: { path: "Run Evaluation via API/Dataset with user questions" }, version: { version_id: promptVersion }, }); } ``` ```python Python evaluation_stats = humanloop.evaluations.get_stats( id=evaluation.id, ) print(evaluation_stats.report) ``` ```typescript TypeScript const evaluationStats = await humanloop.evaluations.getStats(evaluation.id); console.log(evaluationStats.report); ``` Alternatively you can see detailed stats in the Humanloop UI. Navigate to the Prompt, click on the **Evaluations** tab at the top of the page and select the Evaluation you just created. The stats are displayed in the **Stats** tab.
![]()
## Steps To create a dataset from a CSV file, we'll first create a CSV in Google Sheets that contains values for our Prompt variable `{{query}}` and then upload it to a Dataset on Humanloop.
### Create a CSV file. * In our Google Sheets example below, we have a column called `query` which contains possible values for our Prompt variable `{{query}}`. You can include as many columns as you have variables in your Prompt template. * There is additionally a column called `target` which will populate the target output for the classifier Prompt. In this case, we use simple strings to define the target. * More complex Datapoints that contain `messages` and structured objects for targets are supported, but are harder to incorporate into a CSV file as they tend to be hard-to-read JSON. If you need more complex Datapoints, [use the API](./create-dataset-api) instead. ### Commit the dataset Click the **commit** button at the top of the Dataset editor and fill in a commit message. Press **Commit** again.### Export the Google Sheet to CSV In Google Sheets, choose **File** → **Download** → **Comma-separated values (.csv)** ### Create a new Dataset File On Humanloop, select *New* at the bottom of the left-hand sidebar, then select *Dataset*.
### Click **Upload CSV** First name your dataset when prompted in the sidebar, then select the **Upload CSV** button and drag and drop the CSV file you created above using the file explorer. You will then be prompted to provide a commit message to describe the initial state of the dataset.
### Map the CSV columns Map each of the CSV columns into one of `input`, `message`, `target`. To avoid uploading a column of your CSV you can map it to the `exclude` option. To map in columns to Messages, they need to be in a specific format. An example of this can be seen in our example Dataset or below: ``` "[{""role"": ""user"", ""content"": ""Tell me about the weather""}]" ``` Once you have mapped your columns, press **Extend Current Dataset**
### Review your uploaded datapoints You'll see the input-output pairs that were included in the CSV file and you can review the rows to inspect and edit the individual Datapoints.
![]()
Your dataset is now uploaded and ready for use. ## Next steps 🎉 Now that you have Datasets defined in Humanloop, you can leverage our [Evaluations](./overview) feature to systematically measure and improve the performance of your AI applications. See our guides on [setting up Evaluators](./llm-as-a-judge) and [Running an Evaluation](./run-evaluation) to get started. For different ways to create datasets, see the links below: * [Create a Dataset from existing Logs](./create-dataset-from-logs) - useful for curating Datasets based on how your AI application has been behaving in the wild. * [Upload via API](./create-dataset-api) - useful for uploading more complex Datasets that may have nested JSON structures, which are difficult to represent in tabular .CSV format, and for integrating with your existing data pipelines. # Create a Dataset via the API > Learn how to create Datasets in Humanloop to define fixed examples for your projects, and build up a collection of input-output pairs for evaluation and fine-tuning. [Datasets](../../explanation/datasets) are a collection of input-output pairs that can be used to evaluate your Prompts, Tools or even Evaluators. ### Prerequisites If you are using the SDK, the only prerequisite is to have the SDK installed and configured. If you are using the API directly, you will need to have an API key.
First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: ## Steps Using the API is a great way to integrate Humanloop with your existing data pipeline or just to once-off upload a more complex Dataset that is hard to represent in a CSV file, such as one that contains an array of messages and JSON targets.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Post data to the Datasets API We first define some sample data that contains user messages and desired responses from our [Support Agent Prompt](./create-dataset-from-logs) and call the `POST /datasets` endpoint to upload it as follows: ## Next steps 🎉 Now that you have Datasets defined in Humanloop, you can leverage our [Evaluations](./overview) feature to systematically measure and improve the performance of your AI applications. See our guides on [setting up Evaluators](./llm-as-a-judge) and [Running an Evaluation](./run-evaluation) to get started. For different ways to create datasets, see the links below: * [Create a Dataset from existing Logs](./create-dataset-from-logs) - useful for curating Datasets based on how your AI application has been behaving in the wild. * [Upload data from CSV](./upload-dataset-csv) - useful for quickly uploading existing tabular data you've collected outside of Humanloop. # Create a Dataset from existing Logs > Learn how to create Datasets in Humanloop to define fixed examples for your projects, and build up a collection of input-output pairs for evaluation and fine-tuning. [Datasets](../../explanation/datasets) are a collection of input-output pairs that can be used to evaluate your Prompts, Tools or even Evaluators. This guide will show you how to create Datasets in Humanloop from your Logs. ### Prerequisites You should have an existing [Prompt](../../explanation/prompts) on Humanloop and already generated some [Logs](../../explanation/logs). Follow our guide on [creating a Prompt](../prompts/create-prompt). ## Steps To create a Dataset from existing Logs:### Inspect the uploaded Dataset After running this code, in your Humanloop workspace you will now see a Dataset called `Support Query Ground Truth` (or whatever value was in `path`) with your sample data. ![]()
### Navigate to the **Logs** of your Prompt Our Prompt in this example is a Support Agent that answers user queries about Humanloop's product and docs: ## Next steps 🎉 Now that you have Datasets defined in Humanloop, you can leverage our [Evaluations](./overview) feature to systematically measure and improve the performance of your AI applications. See our guides on [setting up Evaluators](./llm-as-a-judge) and [Running an Evaluation](./run-evaluation) to get started. For different ways to create datasets, see the links below: * [Upload data from CSV](./upload-dataset-csv) - useful for quickly uploading existing tabular data you've collected outside of Humanloop. * [Upload via API](./create-dataset-api) - useful for uploading more complex Datasets that may have nested JSON structures, which are difficult to represent in tabular .CSV format, and for integrating with your existing data pipelines. # Set up a code Evaluator > Learn how to create a code Evaluators in Humanloop to assess the performance of your AI applications. This guide covers setting up an offline evaluator, writing evaluation logic, and using the debug console. A code [Evaluator](../../explanation/evaluators) is a Python function that takes a generated [Log](../../explanation/logs) (and optionally a testcase [Datapoint](../../explanation/datasets) if comparing to expected results) as input and returns a **judgement**. The judgement is in the form of a boolean or number that measures some criteria of the generated Log defined within the code. Code Evaluators provide a flexible way to evaluate the performance of your AI applications, allowing you to re-use existing evaluation packages as well as define custom evaluation heuristics. We support a fully featured Python environment; details on the supported packages can be found in the [environment reference](/docs/v5/reference/python-environment) ### Prerequisites You should have an existing [Prompt](../../explanation/prompts) to evaluate and already generated some [Logs](../../explanation/logs). Follow our guide on [creating a Prompt](../../development/guides/create-prompt). In this example, we'll reference a Prompt that categorises a user query about Humanloop's product and docs by which feature it relates to.### Select a subset of the Logs to add Filter logs on a criteria of interest, such as the version of the Prompt used, then multi-select Logs. In the menu in the top right of the page, select **Add to Dataset**.
### Add to a new Dataset Provide a name of the new Dataset and click **Create** (or you can click **add to existing Dataset** to append the selection to an existing Dataset). Then provide a suitable commit message describing the datapoints you've added.
You will then see the new Dataset appear at the same level in the filesystem as your Prompt.
## Create a code Evaluator
### Create a new Evaluator * Click the **New** button at the bottom of the left-hand sidebar, select **Evaluator**, then select **Code**. ## Monitor a Prompt Now that you have an Evaluator, you can use it to monitor the performance of your Prompt by linking it so that it is automatically run on new Logs.* Give the Evaluator a name when prompted in the sidebar, for example `Category Validator`. ### Define the Evaluator code After creating the Evaluator, you will automatically be taken to the code editor. For this example, our Evaluator will check that the feature category returned by the Prompt is from the list of allowed feature categories. We want to ensure our categoriser isn't hallucinating new features. * Make sure the **Mode** of the Evaluator is set to **Online** in the options on the left. * Copy and paste the following code into the code editor: ```python Python ALLOWED_FEATURES = [ "Prompt Editor", "Model Integrations", "Online Monitoring", "Offline Evaluations", "Dataset Management", "User Management", "Roles Based Access Control", "Deployment Options", "Collaboration", "Agents and chaining" ] def validate_feature(log): print(f"Full log output: \n {log['output']}") # Parse the final line of the log output to get the returned category feature = log["output"].split("\n")[-1] return feature in ALLOWED_FEATURES ```
You can define multiple functions in the code Editor to organize your evaluation logic. The final function defined is used as the main Evaluator entry point that takes the Log argument and returns a valid judgement. ### Debug the code with Prompt Logs * In the debug console beneath where you pasted the code, click **Select Prompt or Dataset** and find and select the Prompt you're evaluating. The debug console will load a sample of Logs from that Prompt.* Click the **Run** button at the far right of one of the loaded Logs to trigger a debug run. This causes the code to be executed with the selected Log as input and populates the **Result** column. * Inspect the output of the executed code by selecting the arrow to the right of **Result**.
### Commit the code Now that you've validated the behaviour, commit the code by selecting the **Commit** button at the top right of the Editor and provide a suitable commit message describing your changes. ### Inspect Evaluator logs Navigate to the **Logs** tab of the Evaluator to see and debug all the historic usages of this Evaluator.
![]()
### Link the Evaluator to the Prompt * Navigate to the **Dashboard** of your Prompt * Select the **Monitoring** button above the graph and select **Connect Evaluators**. * Find and select the Evaluator you just created and click **Chose**. ## Evaluating a Dataset When running a code Evaluator on a [Dataset](../../explanation/datasets), you can compare a generated [Log](../../explanation/logs) to each Datapoint's target. For example, here's the code of our example Exact Match code evaluator, which checks that the log output exactly matches our expected target. ```python Python def exact_match(log, testcase): target = testcase["target"]["output"] generation = log["output"] return target == generation ``` ## Next steps * Explore [AI Evaluators](/docs/evaluation/guides/llm-as-a-judge) and [Human Evaluators](/docs/evaluation/guides/human-evaluators) to complement your code-based judgements for more qualitative and subjective criteria. * Combine your Evaluator with a [Dataset](/docs/explanation/datasets) to run [Evaluations](/docs/evaluation/guides/run-evaluation) to systematically compare the performance of different versions of your AI application. # Set up LLM as a Judge > Learn how to use LLM as a judge to check for PII in Logs. LLMs can be used for evaluating the quality and characteristics of other AI-generated outputs. When correctly prompted, LLMs can act as impartial judges, providing insights and assessments that might be challenging or time-consuming for humans to perform at scale. In this guide, we'll explore how to setup an LLM as an [AI Evaluator](../../explanation/evaluators) in Humanloop, demonstrating their effectiveness in assessing various aspects of AI-generated content, such as checking for the presence of Personally Identifiable Information (PII). An AI [Evaluator](../../explanation/evaluators) is a Prompt that takes attributes from a generated [Log](../../explanation/logs) (and optionally from a testcase [Datapoint](../../explanation/dataset) if comparing to expected results) as context and returns a **judgement**. The judgement is in the form of a boolean or number that measures some criteria of the generated Log defined within the Prompt instructions. ### Prerequisites You should have an existing [Prompt](../../explanation/prompts) to evaluate and already generated some [Logs](../../explanation/logs). Follow our guide on [creating a Prompt](../../development/guides/create-prompt). In this example we will use a simple Support Agent Prompt that answers user queries about Humanloop's product and docs.![]()
You can link to a deployed version of the Evaluator by choosing the environment such as `production`, or you can link to a specific version of the Evaluator. If you want changes deployed to your Evaluator to be automatically reflected in Monitoring, link to the environment, otherwise link to a specific version. This linking results in: - An additional graph on your Prompt dashboard showing the Evaluator results over time. - An additional column in your Prompt Versions table showing the aggregated Evaluator results for each version. - An additional column in your Logs table showing the Evaluator results for each Log. ### Generate new Logs Navigate to the **Editor** tab of your Prompt and generate a new Log by entering a query and clicking **Run**. ### Inspect the Monitoring results Navigate to the **Logs** tab of your Prompt and see the result of the linked Evaluator against the new Log. You can filter on this value in order to [create a Dataset](/docs/evaluation/guides/create-dataset) of interesting examples.![]()
## Create an LLM Evaluator
### Create a new Evaluator * Click the **New** button at the bottom of the left-hand sidebar, select **Evaluator**, then select **AI**. * Give the Evaluator a name when prompted in the sidebar, for example `PII Identifier`. ### Define the Evaluator Prompt After creating the Evaluator, you will automatically be taken to the Evaluator editor. For this example, our Evaluator will check whether the request to, or response from, our support agent contains PII. We want to understand whether this is a potential issue that we wish to mitigate with additional [Guardrails](../../observability/alerts-and-guardails) in our agent workflow. * Make sure the **Mode** of the Evaluator is set to **Online** in the options on the left. * Copy and paste the following Prompt into the Editor: ```text You are a helpful assistant. Your job is to observe the requests and outputs to a support agent and identify whether or not they contain any PII. Examples of PII information are: - Names - Addresses - Bank account information - Job information Here is the request and response information: ### Request: {{log.messages}} ### Response: {{log.output_message}} ### Your response should contain the rationale and the final binary true/false verdict as to whether PII exists in the request resposne. The final true/false verdit should be on a new line at the end. ``` ## Next steps * Explore [Code Evaluators](./ocde-based-evaluator) and [Human Evaluators](./human-evaluator) to complement your AI judgements. * Combine your Evaluator with a [Dataset](../../explanation/datasets) to run [Evaluations](./run-evaluation) to systematically compare the performance of different versions of your AI application. # Set up a Human Evaluator > Learn how to set up a Human Evaluator in Humanloop. Human Evaluators allow your subject-matter experts and end-users to provide feedback on Prompt Logs. Human Evaluators allow your subject-matter experts and end-users to provide feedback on Prompt Logs. These Evaluators can be attached to Prompts and Evaluations. ## Creating a Human Evaluator This section will bring you through creating and setting up a Human Evaluator. As an example, we'll use a "Tone" Evaluator that allows feedback to be provided by selecting from a list of options.In the Prompt Editor for an LLM evaluator, you have access to the underlying `log` you are evaluating as well as the `testcase` Datapoint that gave rise to it if you are using a Dataset for **offline** Evaluations. These are accessed with the standard `{{ variable }}` syntax, enhanced with a familiar dot notation to pick out specific values from inside the `log` and `testcase` objects. For example, suppose you are evaluating a Log object like this. ```json { "id": "data_B3RmIu9aA5FibdtXP7CkO", "prompt": {...}, "inputs": { "query": "What is the meaning of life?", }, "messages": [] "output": "I'm sorry, as an AI I don't have the capacity to understand the meaning of life.", "metadata": {...}, ...etc } ``` In the LLM Evaluator Prompt, `{{ log.inputs.query }}` will be replaced with the actual query in the final prompt sent to the LLM Evaluator. In order to get access to the fully populated Prompt that was sent in the underlying Log, you can use the special variable `{{ log_prompt }}`. ### Debug the code with Prompt Logs * In the debug console beneath where you pasted the code, click **Select Prompt or Dataset** and find and select the Prompt you're evaluating. The debug console will load a sample of Logs from that Prompt.* Click the **Run** button at the far right of one of the loaded Logs to trigger a debug run. This causes the Evaluator Prompt to be called with the selected Log attributes as input and populates the **Result** column. * Inspect the output of the executed code by selecting the arrow to the right of **Result**.
### Commit the code Now that you've validated the behaviour, commit the Evaluator Prompt by selecting the **Commit** button at the top right of the Editor and provide a suitable commit message describing your changes. ### Inspect Evaluator logs Navigate to the **Logs** tab of the Evaluator to see and debug all the historic usages of this Evaluator.
![]()
### Create a new Evaluator * Click the **New** button at the bottom of the left-hand sidebar, select **Evaluator**, then select **Human**.  * Give the Evaluator a name when prompted in the sidebar, for example "Tone".  ### Define the Judgment Schema After creating the Evaluator, you will automatically be taken to the Editor. Here, you can define the schema detailing the kinds of judgments to be applied for the Evaluator. The Evaluator will be initialized to a 5-point rating scale by default. In this example, we'll set up a feedback schema for a "Tone" Evaluator. See the [Return types documentation](../../explanation/evaluators#return-types) for more information on return types. * Select **Multi-select** within the **Return type** dropdown. "Multi-select" allows you to apply multiple options to a single Log. * Add the following options, and set the valence for each: * Enthusiastic \[positive] * Informative \[postiive] * Repetitive \[negative] * Technical \[negative] * Update the instructions to "Select all options that apply to the output."  ### Commit and deploy the Evaluator * Click **Commit** in the top-right corner. * Enter "Added initial tone options" as a commit message. Click **Commit**.  * In the "Version committed" dialog, click **Deploy**. * Select the checkbox for you default Environment (usually named "production"), and confirm your deployment.  :tada: You've now created a Human Evaluator that can be used to collect feedback on Prompt Logs. ## Next steps * [Use Human Evaluators in Evaluations](./run-human-evaluation) to collect annotations on Prompt Logs from subject-matter experts. * [Attach Human Evaluators to Prompts](../../observability/guides/capture-user-feedback) to collect end-user feedback # Run a Human Evaluation > Collect judgments from subject-matter experts (SMEs) to better understand the quality of your AI product. In this guide, we'll show how SMEs can provide judgments on Prompt Logs to help you understand the quality of the AI feature. You can then use this feedback to iterate and improve your Prompt performance. ### Prerequisites * You have set up a Human Evaluator appropriate for your use-case. If not, follow our guide to [create a Human Evaluator](/docs/evaluation/guides/human-evaluators). * You have a Dataset with test data to evaluate model outputs against. If not, follow our guide to [create a Dataset from already existing Logs](/docs/evaluation/guides/create-dataset-from-logs). ## Provide judgments on Logs In this guide, we assume you have already created a Prompt and a Dataset for an evaluation. Now we want to leverage the subject-matter experts to help us understand whether model outputs meet our quality standards.### Create a new Evaluation Navigate to the Prompt you want to evaluate and click on the **Evaluation** tab at the top of the page. Click on **Evaluate** to create a new Evaluation. ## Improve the Prompt Explore the Logs that the SME flagged in the Review tab. To make improvements, find a Log with negative judgments and click on its ID above the Log output to open the drawer on the right-hand side. In the drawer, click on the **Editor ->** button to load the Prompt Editor. Now, modify the instructions and commit a new version.### Create a new Run To evaluate a version of your Prompt, click on the **+Run** button, then select the version of the Prompt you want to evaluate and the Dataset you want to use. Click on **+Evaluator** to add a Human Evaluator to the Evaluation.
You can find example Human Evaluators in the **Example Evaluators** folder. Click **Save** to create a new Run. Humanloop will start generating Logs for the Evaluation. ### Apply judgments to generated Logs When Logs are generated, navigate to the **Review** tab. Turn on **Focus mode** and start providing judgments on the generated Logs.
When the last judgment is provided, the Run is marked as complete.
### Review judgments stats You can see the overall performance across all Evaluators in the **Stats** tab.
![]()
Create a new run using the new version of the Prompt and compare the results to find out if the changes have improved the performance. ## Next steps We've successfully collected judgments from the SMEs to understand the quality of our AI product. Explore next: * If your team has multiple internal SMEs, learn how to [effectively manage evaluation involving multiple SMEs](/docs/evaluation/guides/manage-multiple-reviewers). * If SMEs provided negative judgments on the logs, please refer to our guide on [Comparing and Debugging Prompts](/docs/evaluation/guides/comparing-prompt-editor). # Manage multiple reviewers > Learn how to split the work between your SMEs
**Who is this for**: This guide is for large teams that want to leverage their internal subject matter experts (SMEs) to evaluate the performance of their AI features. ### Prerequisites * You have set up [Evaluators](/docs/explanation/evaluators). If not, follow our guide to [create a Human Evaluator](/docs/evaluation/guides/human-evaluators). * You have several subject-matter experts (SMEs) available to provide feedback on Evaluation Logs. ## Divide work between SMEs When you have a large [Dataset](/docs/explanation/datasets) to evaluate, it's helpful to split the work between your SMEs to ensure that the evaluation is completed quickly and effectively.### Split the Dataset into chunks Each Dataset consists of datapoints. Add an identifier to each datapoint to group them into chunks. For example, we [created](https://github.com/humanloop/humanloop-cookbook/blob/main/assets/datasets/dataset_with_common_customer_support_questions.csv) a Dataset with 100 common customer support questions. In the csv file, we added an identifier called "chunk" to each datapoint, splitting the whole Dataset into 10 equal parts. To upload this CSV on Humanloop, create a new Dataset file, then click on the **Upload CSV** button. ## Improve the Prompt With judgments from your SMEs, you can now better understand the model's performance and iterate on your Prompt to improve the model outputs.Alternatively, you [upload Dataset via our SDK](/docs/evaluation/guides/create-dataset#upload-a-dataset-via-api) ### Run an Evaluation Navigate to a Prompt you want to evaluate and create a new Evaluation Run.
### Split the workload between SMEs To split the workload between your SMEs, navigate to the **Review** tab, turn on **Focus mode**, and click on the **Filters** button. Filter the Dataset by identifiers, such as "chunk", to split the review work into smaller pieces. ### Send the URL to your SMEs After you have filtered the Dataset, copy the URL and send it to your SME. When they open the link, they will only see the relevant chunk of the Dataset.
### Monitor progress As the SMEs provide judgments on the outputs, we display the overall progress and the number of outstanding judgments. When the final judgment is given, the Evaluation is marked as complete.
In our example, the SME marked the Log on the right-hand side as "bad" because it's too long. To take action, click on the Log ID above the Log output to open the Log drawer. In the drawer, click on the **Editor ->** button to load this Log in the Prompt Editor. Now, modify the instructions to explicitly state that the model should provide a concise answer. ## Next steps We've successfully split the work among multiple SMEs to effectively evaluate the performance of our AI product. Explore next: * If your SMEs gave negative judgments on the Logs, see our guide on [Comparing and Debugging Prompts](/docs/evaluation/guides/comparing-prompt-editor). * Find out more about [Human Evaluators](/docs/evaluation/guides/human-evaluators) to capture feedback that is most relevant to your use case. # Compare and Debug Prompts > In this guide, we will walk through comparing the outputs from multiple Prompts side-by-side using the Humanloop Editor environment and using diffs to help debugging. You can compare Prompt versions interactively side-by-side to get a sense for how their behaviour differs; before then triggering more systematic [Evaluations](/docs/evaluation/guides/run-evaluation). All the interactions in Editor are stored as Logs within your Prompt and can be inspected further and [added to a Dataset](/docs/evaluation/guides/create-dataset-from-logs) for Evaluations. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. ## Compare Prompt versions In this example we will use a simple support agent Prompt that answers user queries about Humanloop's product and docs.
![]()
### Create a new version of your Prompt Open your Prompt in the Editor and expand **Parameters** and change some details such as the choice of `Model`. In this example, we change from `gpt-4o` to `gpt-4o-mini`. This will create a new uncommitted version of the Prompt. ## View Prompt diff for debugging When debugging more complex Prompts, it's important to understand what changes were made between different versions. Humanloop provides a diff view to support this.Now commit the new version of your Prompt by selecting the blue **Commit** button over **Parameters** and providing a helpful commit message like: ```text Changed model to gpt-4o-mini ``` ### Load up two versions of your Prompt in the Editor To load up the previous version side-by-side, select the menu beside the Load button and select the **New panel** option (depending on your screen real-estate, you can add more than 2 panels).
Then select to *Load* button in the new panel and select another version of your Prompt to compare.
### Compare the outputs of both versions Now you can run the same user messages through both models to compare their behaviours live side-by-side.
![]()
### Navigate to your Prompt dashboard In the sidebar, select the **Dashboard** section under your Prompt file, where you will find a table of all your historic Prompt versions. 1. While in the **Compare** tab, look for the **Diff** section. 2. This section will highlight the changes made between the selected versions, showing additions, deletions, and modifications. 3. Use this diff view to understand how specific changes in your prompt configuration affect the output. By following these steps, you can effectively compare different versions of your Prompts and iterate on your instructions to improve performance. # Set up CI/CD Evaluations > Learn how to automate LLM evaluations as part of your CI/CD pipeline using Humanloop and GitHub Actions. ## Setting up CI/CD Integration with GitHub Actions Integrating Humanloop evaluations into your CI/CD pipeline allows you to automatically test your AI applications as part of your development workflow. This guide will walk you through setting up this integration using GitHub Actions. ### Prerequisites * A GitHub repository for your project * A Humanloop account with access to Evaluations * A Prompt and Dataset set up in Humanloop * An Evaluator configured in Humanloop ## Steps to Set Up CI/CD Integration### Select the versions to compare In the table, select two rows you would like understand the changes between. Then select the **Compare Versions** button above the table.
![]()
### Create a GitHub Actions Workflow In your GitHub repository, create a new file `.github/workflows/humanloop-eval.yml` with the following content: # Spot-check your Logs > Learn how to use the Humanloop Python SDK to sample a subset of your Logs and create an Evaluation Run to spot-check them. By regularly reviewing a sample of your Prompt Logs, you can gain valuable insights into the performance of your Prompts in production, such as through reviews by subject-matter experts (SMEs).This content is currently under development. Please refer to our [V4 documentation](https://docs.humanloop.com/v4) for the current docs. ```yaml ```For real-time observability (typically using code Evaluators), see our guide on setting up [monitoring](../observability/monitoring). This guide describes setting up more detailed evaluations which are run on a small subset of Logs. ### Prerequisites * You have a Prompt with Logs. See our guide on [logging to a Prompt](./prompts/log-to-a-prompt) if you don't yet have one. * You have a Human Evaluator set up. See our guide on [creating a Human Evaluator](./human-evaluators) if you don't yet have one. {/* TODO: This should be Python-only. */}First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: ## Set up an Evaluation```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Create an Evaluation Create an Evaluation for the Prompt. In this example, we also attach a "rating" Human Evaluator so our SMEs can judge the generated responses. ```python evaluation = humanloop.evaluations.create( # Name your Evaluation name="Monthly spot-check", file={ # Replace this with the ID of your Prompt. # You can specify a Prompt by "path" as well. "id": "pr_..." }, evaluators=[ # Attach Evaluator to enable SMEs to rate the generated responses {"path": "Example Evaluators/Human/rating"}, ], ) ``` ### Create a Run Create a Run within the Evaluation. We will then attach Logs to this Run. ```python run = humanloop.evaluations.create_run( id=evaluation.id, ) ``` ### Sample Logs Sample a subset of your Logs to attach to the Run. For this example, we'll sample 100 Logs from the past 30 days, simulating a monthly spot-check. ```python import datetime logs = humanloop.logs.list( file_id="pr_...", # Replace with the ID of the Prompt sample=100, # Example filter to sample Logs from the past 30 days start_date=datetime.datetime.now() - datetime.timedelta(days=30), ) log_ids = [log.id for log in logs] ``` ### Attach Logs to the Run Attach the sampled Logs to the Run you created earlier. ```python humanloop.evaluations.add_logs_to_run( id=evaluation.id, run_id=run.id, log_ids=log_ids, ) ``` You have now created an Evaluation Run with a sample of Logs attached to it. In the Humanloop app, go to the Prompt's Evaluations tab. You should see the new Evaluation named "Monthly spot-check". Click on it to view the Run with the Logs attached.  ## Review your Logs Rate the model generations via the **Review** tab.For further details on how you can manage reviewing your Logs with multiple SMEs, see our guide on [managing multiple reviewers](./manage-multiple-reviewers).  After your Logs have been reviewed, go to the **Stats** tab to view aggregate stats.  ## Repeating the spot-check To repeat this process the next time a spot-check is due, you can create a new Run within the same Evaluation, repeating the above steps from "Create a Run". You will then see the new Run alongside the previous ones in the Evaluation, and can compare the aggregate stats across multiple Runs. ## Next Steps * If you have performed a spot-check and identified issues, you can [iterate on your Prompts in the app](./comparing-prompts) and [run further Evaluations](./run-evaluation) to verify improvements. # Use external Evaluators > Integrate your existing evaluation process with Humanloop. LLM and code Evaluators generally live on the Humanloop runtime environment. The advantage of this is that these Evaluators can be used as [monitoring Evaluators](docs/v5/guides/observability/monitoring) and to allow triggering evaluations [directly from the Humanloop UI](/docs/v5/guides/evals/run-evaluation-ui). Your setup however can be more complex: your Evaluator has library dependencies that are not present in the [runtime environment](/docs/v5/reference/python-environment), your LLM evaluator has multiple reasoning steps, or you prefer managing the logic yourself. External Evaluators address this: they are registered with Humanloop but their code definition remains in your environment. In order to evaluate a Log, you call the logic yourself and send the judgment to Humanloop. In this tutorial, we will build a chat agent that answers questions asked by children, and evaluate its performance using an external Evaluator. ## Create the agent We reuse the chat agent from our [evaluating an agent tutorial](/docs/v5/tutorials/agent-evaluation). Let's create the initial version of our agent. Add the following in a new file:```python title="main.py" maxLines=35 from humanloop import Humanloop from openai import OpenAI from openai.types.chat.chat_completion_message import ChatCompletionMessage as Message import wikipedia import json openai = OpenAI(api_key="ADD YOUR KEY HERE") humanloop = Humanloop(api_key="ADD YOUR KEY HERE") def search_wikipedia(query: str) -> dict: """Search Wikipedia to get up-to-date information for a query.""" try: page = wikipedia.page(query) return { "title": page.title, "content": page.content, "url": page.url, } except Exception as _: return { "title": "", "content": "No results found", "url": "", } def call_model(messages: list[Message]) -> Message: """Calls the model with the given messages""" system_message = { "role": "system", "content": ( "You are an assistant that helps to answer user questions. " "You should leverage wikipedia to answer questions so that " "the information is up to date. If the response from " "Wikipedia does not seem relevant, rephrase the question " "and call the tool again. Then finally respond to the user." ), } response = openai.chat.completions.create( model="gpt-4o", messages=[system_message] + messages, tools=[ { "type": "function", "function": { "name": "search_wikipedia", "description": "Search the internet to get up to date answers for a query.", "parameters": { "type": "object", "required": ["query"], "properties": { "query": {"type": "string"}, }, "additionalProperties": False, }, }, } ], ) return response.choices[0].message.to_dict(exclude_unset=False) def call_agent(question: str) -> str: """Calls the main agent loop and returns the final result""" messages = [{"role": "user", "content": query}] # Retry for a relevant response 3 times at most for _ in range(3): response = call_model(messages) messages.append(response) if response["tool_calls"]: # Call wikipedia to get up-to-date information for tool_call in response["tool_calls"]: source = search_wikipedia( **json.loads(tool_call["function"]["arguments"]) ) messages.append( { "role": "tool", "content": json.dumps(source), "tool_call_id": tool_call["id"], } ) else: # Respond to the user return response["content"] if __name__ == "__main__": result = call_agent("Where does the sun go at night?") print(result) ``` ```typescript title="main.ts" maxLines=35 import { HumanloopClient } from "humanloop"; import OpenAI from "openai"; import type { ChatCompletionMessageParam as Message } from "openai/resources"; import wikipedia from "wikipedia"; import fs from "fs"; import readline from "readline"; const openai = new OpenAI({ apiKey: " Run the agent and check if it works:" }); const humanloop = new HumanloopClient({ apiKey: " " }); type WikiResult = { title: string; content: string; url: string; }; const searchWikipedia = async (query: string) => { try { const page = await wikipedia.page(query); const NO_RESULT_FOUND: WikiResult = { title: "", content: "No results found", url: "", }; if (page) { return { title: page?.title || "", content: (await page?.content()) || "", url: `https://en.wikipedia.org/wiki/${encodeURIComponent( page?.title || "" )}`, } as WikiResult; } return NO_RESULT_FOUND; } catch (error) { return NO_RESULT_FOUND; } }; const callModel = async (messages: Array ) => { const systemMessage: Message = { role: "system", content: "You are an assistant that helps to answer user questions. " + "You should leverage wikipedia to answer questions so that " + "the information is up to date. If the response from " + "Wikipedia does not seem relevant, rephrase the question " + "and call the tool again. Then finally respond to the user.", }; const response = await openai.chat.completions.create({ model: "gpt-4o", messages: [systemMessage, ...messages], tools: [ { type: "function", function: { name: "search_wikipedia", description: "Search the internet to get up to date answers for a query.", parameters: { type: "object", required: ["query"], properties: { query: { type: "string" }, }, additionalProperties: false, }, }, }, ], }); return response.choices[0].message; } async function callAgent({ question }: { question: string }): Promise { const messages: Message[] = [{ role: "user", content: question }]; for (let _ = 0; _ < 3; _++) { const response = await callModel(messages); messages.push(response); if (response.tool_calls) { for (const toolCall of response.tool_calls) { const args = JSON.parse(toolCall.function.arguments); const source = await searchWikipedia(args.query); messages.push({ role: "tool", content: JSON.stringify(source), tool_call_id: toolCall.id, }); } } else { return response.content || ""; } } return "Could not get a relevant response after multiple attempts."; } async function main() { const result = await callAgent({ question: "Where does the sun go at night?", }); console.log(result); } main(); ``` ## Evaluate the agent ```bash python main.py ``` ```plaintext Okay! Imagine the Earth is like a big ball, and we live on it. The sun doesn't really “go” anywhere—it stays in the same spot, shining all the time. But our Earth is spinning like a top! ``` ```bash npx tsx main.ts ``` ```plaintext Okay! Imagine the Earth is like a big ball, and we live on it. The sun doesn't really “go” anywhere—it stays in the same spot, shining all the time. But our Earth is spinning like a top! ``` Evaluators are callables that take the Log's dictionary representation as input and return a judgment. The Evaluator's judgment should respect the `return_type` present in Evaluator's [specification](https://humanloop.com/docs/v5/api-reference/evaluators/upsert#request.body.spec). The Evaluator can take an additional `target` argument to compare the Log against. The target is provided in an Evaluation context by the validation [Dataset](/docs/v5/explanation/datasets). For more details, check out our [Evaluator explanation](/docs/v5/explanation/evaluators). ### Define external Evaluator The Evaluator takes a `log` argument, which represents the Log created by calling `call_agent`. Let's add a simple Evaluator that checks if the agent's answers are too long. Add this in the `agent.py` file: ```python if __name__ == "__main__": def easy_to_understand(log): return len(log["output"]) < 100 ``` ### Add dataset Create a file called `dataset.jsonl` and add the following: ```jsonl title="dataset.jsonl" maxLines=5 {"inputs": {"question": "Why is the sky blue?"}} {"inputs": {"question": "Where does the sun go at night?"}} {"inputs": {"question": "Why do birds fly?"}} {"inputs": {"question": "What makes rainbows?"}} {"inputs": {"question": "Why do we have to sleep?"}} {"inputs": {"question": "How do fish breathe underwater?"}} {"inputs": {"question": "Why do plants need water?"}} {"inputs": {"question": "How does the moon stay in the sky?"}} {"inputs": {"question": "What are stars made of?"}} {"inputs": {"question": "Why do we have seasons?"}} {"inputs": {"question": "How does the TV work?"}} {"inputs": {"question": "Why do dogs wag their tails?"}} {"inputs": {"question": "What makes cars go?"}} {"inputs": {"question": "Why do we need to brush our teeth?"}} {"inputs": {"question": "What do ants eat?"}} {"inputs": {"question": "Why does the wind blow?"}} {"inputs": {"question": "How do airplanes stay in the air?"}} {"inputs": {"question": "Why does the ocean look so big?"}} {"inputs": {"question": "What makes the grass green?"}} {"inputs": {"question": "Why do we have to eat vegetables?"}} {"inputs": {"question": "How do butterflies fly?"}} {"inputs": {"question": "Why do some animals live in the zoo?"}} {"inputs": {"question": "How do magnets stick to the fridge?"}} {"inputs": {"question": "What makes fire hot?"}} {"inputs": {"question": "Why do leaves change color?"}} {"inputs": {"question": "What happens when we flush the toilet?"}} {"inputs": {"question": "Why do we have belly buttons?"}} {"inputs": {"question": "What makes the clouds move?"}} {"inputs": {"question": "Why do we have eyebrows?"}} {"inputs": {"question": "How do seeds turn into plants?"}} {"inputs": {"question": "Why does the moon change shape?"}} {"inputs": {"question": "Why do bees make honey?"}} {"inputs": {"question": "What makes ice melt?"}} {"inputs": {"question": "Why do we sneeze?"}} {"inputs": {"question": "How do trains stay on the tracks?"}} {"inputs": {"question": "Why do stars twinkle?"}} {"inputs": {"question": "Why can't we see air?"}} {"inputs": {"question": "What makes the Earth spin?"}} {"inputs": {"question": "Why do frogs jump?"}} {"inputs": {"question": "Why do cats purr?"}} {"inputs": {"question": "How do phones let us talk to people far away?"}} {"inputs": {"question": "Why does the moon follow us?"}} {"inputs": {"question": "What makes lightning?"}} {"inputs": {"question": "Why does it snow?"}} {"inputs": {"question": "Why do we have shadows?"}} {"inputs": {"question": "Why do boats float?"}} {"inputs": {"question": "What makes our heart beat?"}} {"inputs": {"question": "Why do some animals sleep all winter?"}} {"inputs": {"question": "Why do we have to wear shoes?"}} {"inputs": {"question": "What makes music?"}} ``` ### Add Evaluation Instantiate an Evaluation using the client's \[]`evaluations.run`]\(/docs/v5/sdk/run-evaluation) utility. `easy_to_understand` is an external Evaluator, so we provide its definition via the `callable` argument. At runtime, `evaluations.run` will call the function and submit the judgment to Humanloop. ```python title="agent.py" maxLines=100 highlight={5-28} if __name__ == "__main__": def easy_to_understand(log): return len(log["output"]) < 100 # Read the evaluation dataset with open("dataset.jsonl", "r") as fp: dataset = [json.loads(line) for line in fp] humanloop.evaluations.run( name="QA Agent Answer Comprehensiveness", file={ "path": "QA Agent/Agent", "callable": call_agent, }, evaluators=[ { "path": "QA Agent/Comprehension", "callable": easy_to_understand, "args_type": "target_free", "return_type": "boolean", } ], dataset={ "path": "QA Agent/Children Questions", "datapoints": dataset, }, workers=8, ) ``` ### Run the evaluation ## Add detailed loggingClick on the link to see the results when the Evaluation is complete. ```bash title="Terminal" python main.py ``` ```bash title="Terminal" maxLines=50 Navigate to your Evaluation: https://app.humanloop.com/project/fl_9CCIoTzySPfUFeIxfYE6g/evaluations/evr_67tEc2DiR83fy9iTaqyPA/stats Flow Version ID: flv_9ECTrfeZYno2OIj9KAqlz Run ID: rn_67tEcDYV6mqUS86hD8vrP Running 'Agent' over the Dataset 'Children Questions' using 8 workers [##############--------------------------] 15/50 (30.00%) | ETA: 14 ... 📊 Evaluation Results for QA Agent/Agent +------------------------+---------------------+ | | Latest | +------------------------+---------------------+ | Run ID | 67tEc | +------------------------+---------------------+ | Version ID | 9ECTr | +------------------------+---------------------+ | Added | 2024-11-19 21:49:02 | +------------------------+---------------------+ | Evaluators | | +------------------------+---------------------+ | QA Agent/Comprehension | 3.24 | +------------------------+---------------------+ ``` ```bash title="Terminal" maxLines=50 npx tsx main.ts ``` ```bash title="Terminal" maxLines=50 Navigate to your Evaluation: https://app.humanloop.com/project/fl_9CCIoTzySPfUFeIxfYE6g/evaluations/evr_67tEc2DiR83fy9iTaqyPA/stats Flow Version ID: flv_9ECTrfeZYno2OIj9KAqlz Run ID: rn_67tEcDYV6mqUS86hD8vrP Running 'Agent' over the Dataset 'Children Questions' using 8 workers [##############--------------------------] 15/50 (30.00%) | ETA: 14 ... 📊 Evaluation Results for QA Agent/Agent +------------------------+---------------------+ | | Latest | +------------------------+---------------------+ | Run ID | 67tEc | +------------------------+---------------------+ | Version ID | 9ECTr | +------------------------+---------------------+ | Added | 2024-11-19 21:49:02 | +------------------------+---------------------+ | Evaluators | | +------------------------+---------------------+ | QA Agent/Comprehension | 3.24 | +------------------------+---------------------+ ``` If you use a programming language not supported by the SDK, or want more control, see our guide on [logging through the API](/docs/v5/guides/observability/logging-through-api) for an alternative to decorators. Up to this point, we have treated the agent as a black box, reasoning about its behavior by looking at the inputs and outputs. Let's use Humanloop logging to observe the step-by-step actions taken by the agent.Evaluate the agent again. When it's done, head to your workspace and click the **Agent** [Flow](/docs/v5/guides/explanations/flows) on the left. Select the Logs tab from the top of the page. Modify `main.py`: ```python title="main.py" maxLines=100 highlight={1,5,10,15} @humanloop.tool(path="QA Agent/Search Wikipedia") def search_wikipedia(query: str) -> dict: ... @humanloop.prompt(path="QA Agent/Prompt") def call_model(messages: list[Message]) -> Message: ... @humanloop.flow(path="QA Agent/Agent") def call_agent(question: str) -> str: ... ``` To auto-instrument calls to OpenAI, pass the module in the Humanloop constructor: ```typescript const humanloop = new HumanloopClient({ apiKey: process.env.HUMANLOOP_API_KEY, providers: { // Pass the OpenAI module, not the initialized client OpenAI } }); ``` Modify `main.ts`: ```typescript title="main.ts" maxLines=100 highlight={20-21,26-27,32-33} const searchWikipedia = humanloop.tool({ path: "QA Agent/Search Wikipedia", version: { function: { name: "Search Wikipedia", description: "Search Wikipedia for the best article to answer a question", strict: true, parameters: { type: "object", properties: { query: { type: "string", description: "The question to search Wikipedia for", }, }, required: ["query"], }, }, }, // Wraps the initial function body callable: async ({ query }) => { ... }, }); const callModel = humanloop.prompt({ path: "QA Agent/Prompt", // Wraps the initial function body callable: async ({ messages }) => { ... }, }); const callAgent = humanloop.flow({ path: "QA Agent/Agent", // Wraps the initial function body callable: async ({ question }) => { ... }, }); ```The decorators divide the code in logical components, allowing you to observe the steps taken to answer a question. Every step taken by the agent creates a Log. ## Next steps You've learned how to integrate your existing evaluation process with Humanloop. Learn more about Humanloop's features in these guides: * Learn how to use Evaluations to improve on your feature's performance in our [tutorial on evaluating a chat agent](/docs/v5/tutorials/agent-evaluation). * Evals work hand in hand with logging. Learn how to log detailed information about your AI project in [logging setup guide](/docs/v5/quickstart/set-up-logging). # Evaluate external logs > Run an Evaluation on Humanloop with your own This guide demonstrates how to run an Evaluation on Humanloop using your own logs. This is useful if you have existing logs in an external system and want to evaluate them on Humanloop with minimal setup. In this guide, we will use the example of a JSON file containing chat messages between users and customer support agents. This guide will bring you through uploading these logs to Humanloop and creating an Evaluation with them. ## Prerequisites The code in this guide uses the Python SDK. To follow along, you will need to have the SDK installed and configured. While the code snippets are in Python, the same steps can be performed using the TypeScript SDK or via the API directly. If you are using the API directly, you will need to have an API key.
First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: The example JSON data in this guide can be found in the [Humanloop Cookbook](https://github.com/humanloop/humanloop-cookbook/tree/evaluating-external-logs/assets). To continue with the code in this guide, download `conversations-a.json` and `conversations-b.json` from the `assets` folder. ## Evaluate your external logs We'll start by loading data from `conversations-a.json`, which represents logs recorded by an external system. ```python import json with open("conversations-a.json") as f: data = json.load(f) ``` In this example, `data` is a list of chat messages between users and a support agent.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Upload logs to Humanloop ## Next steps The above examples demonstrate how you can quickly populate an Evaluation Run with your logs. * You can extend this Evaluation with custom Evaluators, such as using [Code Evaluators](./code-based-evaluator) to calculate metrics, or using [Human Evaluators](./human-evaluators) to set up your Logs to be reviewed by your subject-matter experts. * Now that you've set up an Evaluation, explore the other [File](../../explanation/files) types on Humanloop to see how they can better reflect your production systems, and how you can use Humanloop to version-control them. Here, we've used a [Flow](../../explanation/flows) to represent a black-box system. # Create a Prompt > Learn how to create a Prompt in Humanloop using the UI or SDK, version it, and use it to generate responses from your AI models. Prompt management is a key part of the Humanloop platform. Humanloop acts as a registry of your [Prompts](/docs/explanation/prompts) so you can centrally manage all their versions and [Logs](/docs/explanation/logs), and evaluate and improve your AI systems. This guide will show you how to create a Prompt [in the UI](./create-prompt#create-a-prompt-in-the-ui) or [via the SDK/API](./create-prompt#create-a-prompt-using-the-sdk).These steps are suitable if you do not already have an Evaluation on Humanloop. The [Upload new logs step](#upload-new-logs) demonstrates a simpler process if you already have an Evaluation you want to add a new set of logs to. Upload the logs with the `log(...)` method. This will automatically create a [Flow](../../explanation/flows) on Humanloop. We additionally pass in some `attributes` identifying the configuration of the system that generated these logs. `attributes` accepts arbitrary values, and is used for versioning the Flow. Here, it allows us to associate this set of logs with a specific version of the support agent. ```python log_ids = [] for messages in data: log = humanloop.flows.log( path="External logs demo/Travel planner", flow={"attributes": {"agent-version": "1.0.0"}}, # Optionally add attributes to identify this version of the support agent. messages=messages, ) log_ids.append(log.id) ``` This will have created a new Flow on Humanloop named **Travel planner**. To confirm this logging has succeeded, navigate to the **Logs** tab of the Flow and view the uploaded logs. Each Log should correspond to a conversation and contain a list of messages.  We will also use the created Flow version when creating our Run. ```python version_id = log.version_id ``` ### Create an Evaluation Run Next, create an Evaluation on Humanloop. Within the Evaluation, create a Run which will contain the Logs. Here, we'll use the example "Helpfulness" LLM-as-a-judge Evaluator. This will automatically rate the helpfulness of the support agent across our logs. ```python evaluation = humanloop.evaluations.create( name="Past records", # NB: you can use `path`or `id` for references on Humanloop file={"path": "External logs demo/Travel planner"}, evaluators=[ # Replace with your Evaluators {"path": "Example Evaluators/AI/Helpfulness"}, ], ) run = humanloop.evaluations.create_run( id=evaluation.id, version={'version_id': version_id}, # Associate this Run to the Flow version created above. ) ``` ### Assign Logs to the Run Finally, add the Flow Logs to the Run. ```python humanloop.evaluations.add_logs_to_run( id=evaluation.id, run_id=run.id, log_ids=log_ids, ) ``` ### Review the Evaluation You have now created an Evaluation on Humanloop and added Logs to it.  Go to the Humanloop UI to view the Evaluation. Within the Evaluation, go to **Logs** tab. Here, you can view your uploaded logs as well as the Evaluator judgments.  The following steps will guide you through adding a different set of logs to a new Run for comparison. ### Upload new logsIf you already have an Evaluation that you want to add a new set of logs to, you can start from here. To start from this point, retrieve the ID of the Evaluation you want to add logs to. Go to the Evaluation you want to add logs to on the Humanloop UI and copy the ID from the URL. This is the segment of the URL after `evaluations/`, e.g. `evr_...`. Now that we have an Evaluation on Humanloop, we can add a separate set of logs to it and compare the performance across this set of logs to the previous set. While we can achieve this by repeating the above steps, we can add logs to a Run in a more direct and simpler way now that we have an existing Evaluation. For this example, we'll continue with the Evaluation created in the previous section, and add a new Run with the data from `conversations-b.json`. These represent a set of logs from a prototype version of the support agent. ```python with open("conversations-b.json") as f: data = json.load(f) ``` #### Create a new Run Create a new Run within the Evaluation that will contain this set of logs. ```python run = humanloop.evaluations.create_run( id=evaluation.id, ) ``` #### Log to the Run Pass the `run_id` argument in your `log(...)` call to associate the Log with the Run. ```python # Add the new data to the Run for messages in data: log = humanloop.flows.log( path="External logs demo/Travel planner", flow={"attributes": {"agent-version": "2.0.0"}}, messages=messages, # Pass `run_id` to associate the Log with the Run. run_id=run.id, ) ``` ### Compare the results View the Evaluation on Humanloop. It will now contain two Runs. In the **Stats** tab of the Evaluation, you can now compare the performance of the two sets of logs. In our case, our second set of logs (on the right) can be seen to be less helpful.  **Prerequisite**: A Humanloop account. You can create an account now by going to the [Sign up page](https://app.humanloop.com/signup). ## Create a Prompt in the UI#### Create a Humanloop Account If you haven’t already, create an account or log in to Humanloop #### Add an OpenAI API Key If you’re the first person in your organization, you’ll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys) 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider. ## Get StartedUsing the Prompt Editor will use your OpenAI credits in the same way that the OpenAI playground does. Keep your API keys for Humanloop and the model providers private. ### Create a Prompt File When you first open Humanloop you’ll see your File navigation on the left. Click ‘**+ New**’ and create a **Prompt**. *** ## Create a Prompt using the SDK The Humanloop Python SDK allows you to programmatically create and version your [Prompts](/docs/explanation/prompts) in Humanloop, and log generations from your models. This guide will show you how to create a Prompt using the SDK. Note that you can also version your prompts dynamically with every PromptIn the sidebar, rename this file to "Comedian Bot" now or later. ### Create the Prompt template in the Editor The left hand side of the screen defines your Prompt – the parameters such as model, temperature and template. The right hand side is a single chat session with this Prompt.
Click the “**+ Message**” button within the chat template to add a system message to the chat template.
Add the following templated message to the chat template. ``` You are a funny comedian. Write a joke about {{topic}}. ``` This message forms the chat template. It has an input slot called `topic` (surrounded by two curly brackets) for an input value that is provided each time you call this Prompt. On the right hand side of the page, you’ll now see a box in the **Inputs** section for `topic`. 1. Add a value for `topic` e.g. music, jogging, whatever 2. Click **Run** in the bottom right of the page This will call OpenAI’s model and return the assistant response. Feel free to try other values, the model is *very* funny. You now have a first version of your prompt that you can use. ### Commit your first version of this Prompt 1. Click the **Commit** button 2. Put “initial version” in the commit message field 3. Click **Commit**
### View the logs Under the Prompt File, click ‘Logs’ to view all the generations from this Prompt Click on a row to see the details of what version of the Prompt generated it. From here you can give feedback to that generation, see performance metrics, open up this example in the Editor, or add this log to a Dataset.
![]()
**Prerequisite**: A Humanloop SDK Key. You can get this from your [Organisation Settings page](https://app.humanloop.com/account/api-keys) if you have the [right permissions](/docs/v5/reference/access-roles). First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: After initializing the SDK client, you can call the Prompt creation endpoint.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Create the Prompt This can be done by using the [Prompt Upsert](/docs/v5/api-reference/prompts/upsert) method in the SDK. Or by calling the API directly: You now have a Prompt in Humanloop that contains your initial version. You can call the Prompt in Editor and invite team members by going to your organization's members page. ## Next Steps With the Prompt set up, you can now integrate it into your app by following the [Call a Prompt Guide](/docs/development/guides/call-prompt). # Call a Prompt > Learn how to call your Prompts that are managed on Humanloop. This guide will show you how to call your Prompts through the API, enabling you to generate responses from the large language model while versioning your Prompts. You can call an existing Prompt on Humanloop, or you can call a Prompt you're managing in code. These two use-cases are demonstrated below. ### Prerequisites### Go to the App Go to the [Humanloop app](https://app.humanloop.com) and you will see your Prompt in your list of files. First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: ## Call an existing Prompt If you don't have Prompt already on Humanloop, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Get the Prompt ID In Humanloop, navigate to the Prompt and copy the Prompt ID by clicking Prompt name in the top bar, and copying from the popover. ## Call a Prompt defined in code You can also manage your Prompts in code. Pass the `prompt` details within your API call to generate responses with the specified parameters.### Call the Prompt by ID Now you can use the SDK to generate completions and log the results to your Prompt: This can be done by using the [Prompt Call](/docs/v5/api-reference/prompts/call) method in the SDK.
## View your Prompt Logs Navigate to the **Logs** tab of your Prompt. You will be able to see the recorded inputs, messages and model generations. ## Next steps * [Iterate and improve on your Prompts](../evals/comparing-prompts) in the Editor * [Capture end-user feedback](../observability/capture-user-feedback) to monitor your model performance. # Log to a Prompt > Learn how to create a Prompt in Humanloop using the UI or SDK, version it, and use it to generate responses from your AI models. Prompt management is a key part of the Humanloop platform. This guide will show you how to capture the [Logs](/docs/explanation/logs) of your LLM calls into Humanloop. The easiest way to log LLM generations to Humanloop is to use the `Prompt.call()` method (see the guide on [Calling a Prompt](/docs/development/guides/call-prompt)). You will only need to supply prompt ID and the inputs needed by the prompt template, and the endpoint will handle fetching the latest template, making the LLM call and logging the result. However, there may be scenarios that you wish to manage the LLM provider calls directly in your own code instead of relying on Humanloop. For example, you may be using an LLM provider that is not directly supported by Humanloop such as a custom self-hosted model, or you may want to avoid adding Humanloop to the critical path of the LLM API calls. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: ## Log data to your Prompt To log LLM generations to Humanloop, you will need to make a call to the `/prompts/log` endpoint. Note that you can either specify a version of the Prompt you are logging against - in which case you will need to take care that you are supplying the correct version ID and inputs. Or you can supply the full prompt and a new version will be created if it has not been seen before.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ### Get your Prompt Fetch a Prompt from Humanloop by specifying the ID. You can ignore this step if your prompts are created dynamically in code. # Tool calling in Editor > Learn how to use tool calling in your large language models and intract with it in the Humanloop Prompt Editor. Humanloop's Prompt Editor supports for Tool Calling functionality, enabling models to interact with external functions. This feature, akin to [OpenAI's function calling](https://platform.openai.com/docs/guides/function-calling), is implemented through JSON Schema tools in Humanloop. These Tools adhere to the widely-used JSON Schema syntax, providing a standardized way to define data structures. Within the editor, you have the flexibility to create inline JSON Schema tools as part of your model configuration. This capability allows you to establish a structured framework for the model's responses, enhancing control and predictability. Throughout this guide, we'll explore the process of leveraging these tools within the editor environment. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. ## Create and use a tool in the Prompt Editor To create and use a tool follow the following steps:Here's how to do this in code: ### Call your Prompt This can be your own model, or any other LLM provider. Here is an example of calling OpenAI: ```python from humanloop import Humanloop, prompt_utils PROMPT_ID = " " hl = Humanloop(api_key=" ") prompt = hl.prompts.get(id=PROMPT_ID) # This will fill the prompt template with the variables template = prompt_utils.populate_template(prompt.template, {"language": "Python"}) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; import { ChatMessage } from "humanloop/api"; const humanloop = new HumanloopClient({ apiKey: " ", }); const prompt = await humanloop.prompts.get(" "); function fillTemplate( template: ChatMessage[], variables: { [key: string]: string } ): ChatMessage[] { const replaceVariable = (match: string, variable: string) => { const trimmedVariable = variable.trim(); if (trimmedVariable in variables) { return variables[trimmedVariable]; } else { throw new Error(`Error: Variable '${trimmedVariable}' is missing.`); } }; return template.map((message) => { if (typeof message.content !== "string") { return message; } const filledContent = message.content.replace( /\{\{\s*(.*?)\s*\}\}/g, replaceVariable ); return { ...message, content: filledContent }; }); } const template = fillTemplate(prompt.template as ChatMessage[], { language: "Python", }); ``` ### Log the result Finally, log the result to your project: ```python import openai client = openai.OpenAI(api_key=" ") messages = template + [{"role": "user", "content": "explain how async works"}] chat_completion = client.chat.completions.create( messages=messages, model=prompt.model, temperature=prompt.temperature ) ``` ```typescript import { OpenAI } from "openai"; import { ChatCompletionMessageParam } from "openai/resources"; const client = new OpenAI({ apiKey: " ", }); const messages = template.concat([ { role: "user", content: "explain how async works" }, ]); const chatCompletion = await client.chat.completions.create({ messages: messages as ChatCompletionMessageParam[], model: prompt.model, temperature: prompt.temperature, }); ``` ```python # Parse the output from the OpenAI response. output_message = chat_completion.choices[0].message # Log the inputs, outputs and config to your project. log = hl.prompts.log( id=PROMPT_ID, output_message=output_message, messages=messages, ) ``` ```typescript // Get the output from the OpenAI response. const outputMessage = chatCompletion.choices[0].message; const log = humanloop.prompts.log({ id: PROMPT_ID, outputMessage: outputMessage, messages: messages, }); ``` ### **Open the editor** Go to a Prompt and open the Editor. ### **Select a model that supports Tool Calling** Congratulations! You've successfully learned how to use tool calling in the Humanloop editor. This powerful feature allows you to simulate and test tool interactions, helping you create more dynamic and context-aware AI applications. Keep experimenting with different scenarios and tool responses to fully explore the capabilities of your AI model and create even more impressive applications! ## Next steps After you've created and tested your tool configuration, you might want to reuse it across multiple prompts. Humanloop allows you to link a tool, making it easier to share and manage tool configurations. For more detailed instructions on how to link and manage tools, check out our guide on [Linking a JSON Schema Tool](/docs/development/guides/link-json-schema-tool). # Re-use snippets in Prompts > Learn how to use the Snippet tool to manage common text snippets that you want to reuse across your different prompts. The Snippet Tool supports managing common text 'snippets' that you want to reuse across your different prompts. A Snippet tool acts as a simple key/value store, where the key is the name of the common re-usable text snippet and the value is the corresponding text. For example, you may have some common persona descriptions that you found to be effective across a range of your LLM features. Or maybe you have some specific formatting instructions that you find yourself re-using again and again in your prompts. Instead of needing to copy and paste between your editor sessions and keep track of which projects you edited, you can instead inject the text into your prompt using the Snippet tool. ## Create and use a Snippet Tool ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. To create and use a snippet tool, follow the following steps:To view the list of models that support Tool calling, see the [Models page](/docs/reference/models#models). In the editor, you'll see an option to select the model. Choose a model like `gpt-4o` which supports Tool Calling. ### **Define the Tool** To get started with tool definition, it's recommended to begin with one of our preloaded example tools. For this guide, we'll use the `get_current_weather` tool. Select this from the dropdown menu of preloaded examples. If you choose to edit or create your own tool, you'll need to use the universal [JSON Schema syntax](https://json-schema.org/). When creating a custom tool, it should correspond to a function you have defined in your own code. The JSON Schema you define here specifies the parameters and structure you want the AI model to use when interacting with your function.### **Test it out** Now, let's test our tool by inputting a relevant query. Since we're working with a weather-related tool, try typing: `What's the weather in Boston?`. This should prompt OpenAI to respond using the parameters we've defined.
Keep in mind that the model's use of the tool depends on the relevance of the user's input. For instance, a question like '*how are you today?*' is unlikely to trigger a weather-related tool response. ### **Check assistant response for a tool call** Upon successful setup, the assistant should respond by invoking the tool, providing both the tool's name and the required data. For our `get_current_weather` tool, the response might look like this: ``` get_current_weather({ "location":"Boston, MA" }) ``` ### **Input tool response** After the tool call, the editor will automatically add a partially filled tool message for you to complete. You can paste in the exact response that the Tool would respond with. For prototyping purposes, you can also just simulate the response yourself. Provide in a mock response: To input the tool response: 1. Find the tool response field in the editor. 2. Enter the response matching the expected format, such as: ```json { "temperature": 12, "condition": "drizzle", "unit": "celsius" } ``` Remember, the goal is to simulate the tool's output as if it were actually fetching real-time weather data. This allows you to test and refine your prompt and tool interaction without needing to implement the actual weather API. ### **Submit tool response** After entering the simulated tool response, click on the 'Run' button to send the Tool message to the AI model. ### **Review assistant response** The assistant should now respond using the information provided in your simulated tool response. For example, if you input that the weather in Boston was drizzling at 12°C, the assistant might say: `The current weather in Boston, MA is 12°C with drizzle.` This response demonstrates how the AI model incorporates the tool's output into its reply, providing a more contextual and data-driven answer.### **Iterate and refine** Feel free to experiment with different queries and simulated tool responses. This iterative process helps you fine-tune your prompt and understand how the AI model interacts with the tool, ultimately leading to more effective and accurate responses in your application. ### **Save your Prompt** By saving your prompt, you're creating a new version that includes the tool configuration.
### Create a new Snippet Tool ### Name the Tool Name it `assistant-personalities` and give it a description `Useful assistant personalities`. ### Add a key called "helpful-assistant" In the initial box add `helpful-assistant` and give it a value of `You are a helpful assistant. You like to tell jokes and if anyone asks your name is Sam.` ### Add another key called "grumpy-assistant" Let's add another key-value pair, so press the **Add a key/value pair** button and add a new key of `grumpy-assistant` and give it a value of `You are a grumpy assistant. You rarely try to help people and if anyone asks your name is Freddy.`.
### Commit and Deploy your Tool. Press the *Commit button*, and enter a commit message for this new version. When asked if to Deploy your version, click the deploy button and deploy to your *production* environment.
Now your Snippets are set up, you can use it to populate strings in your prompt templates across your projects. ### Navigate to the **Editor** Go to the Editor of your previously created project. ### Add `{{ assistant-personalities(key) }}` to your prompt Delete the existing prompt template and add `{{ assistant-personalities(key) }}` to your prompt.
Double curly bracket syntax is used to call a tool in the editor. Inside the curly brackets you put the tool name, e.g. `{{ my-tool-name(key) }}`. ### Enter the key as an input In the input area set the value to `helpful-assistant`. The tool requires an input value to be provided for the key. When adding the tool an inputs field will appear in the top right of the editor where you can specify your `key`. ### Press the **Run** button Start the chat with the LLM and you can see the response of the LLM, as well as, see the key you previously defined add in the Chat on the right.### Change the key to `grumpy-assistant`.
If you want to see the corresponding snippet to the key you either need to first run the conversation to fetch the string and see it in the preview. ### Play with the LLM Ask the LLM, `I'm a customer and need help solving this issue. Can you help?'`. You should see a grumpy response from "Freddy" now. If you have a specific key you would like to hardcode in the prompt, you can define it using the literal key value: `{{("key") }}`, so in this case it would be `{{ assistant-personalities("grumpy-assistant") }}`. Delete the `grumpy-assistant` field and add it into your chat template. ### **Save** your Prompt. If you're happy with you're grumpy assistant, Commit this new version of your Prompt. The Snippet tool is particularly useful because you can define passages of text once in a Snippet tool and reuse them across multiple prompts, without needing to copy/paste them and manually keep them all in sync. Editing the values in your tool allows the changes to automatically propagate to the Prompts when you update them, as long as the key is the same.
Since the values for a Snippet are saved on the Tool, not the Prompt, changing the values (or keys) defined in your Snippet tools can affect the Prompt's behaviour in way that won't be captured by the Prompt's version. This could be exactly what you intend, however caution should still be used make sure the changes are expected. # Deploy to an environment > Environments enable you to deploy model configurations and experiments, making them accessible via API, while also maintaining a streamlined production workflow. [Environments](/docs/explanation/environments) are a tagging system for deploying Prompts. They enable you to deploy maintain a streamlined deployment workflow and keep track of different versions of Prompts. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. To deploy a version to an environment:### Navigate to the **Dashboard** of your Prompt ### Click the dropdown menu of the environment. # Create a Directory > Directories can be used to group together related files. This is useful for organizing your work. This guide will show you how to create a [Directory](/docs/explanation/directories) in the UI. A directory is a collection of files and other directories.### Click the **Change deployment** button ### Select a version Choose the version you want to deploy from the list of available versions.
### Click the **Deploy** button.
**Prerequisite**: A Humanloop account. You can create an account now by going to the [Sign up page](https://app.humanloop.com/signup). ## Create a Directory### Create a Directory 1. Open Humanloop app. 2. Click '**+ New**' on the left and select **Directory**. 3. Name your new directory, for example, "Summarization App". You have now successfully created a directory and moved a file into it. This organization can help you manage your AI applications more efficiently within Humanloop. # Link a Tool to a Prompt > Learn how to create a JSON Schema tool that can be reused across multiple Prompts. It's possible to re-use tool definitions them across multiple Prompts. You achieve this by having a Prompt file which defines a JSON schema, and linking them to your Prompt. You achieve this by creating a `JSON Schema` Tool and linking that to as many Prompts as you need. Importantly, updates to this Tool defined here will then propagate automatically to all the Prompts you've linked it to, without having to deploy new versions of the Prompt. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. ## Creating and linking a JSON Schema Tool To create a reusable JSON Schema tool for your organization, follow these steps:You can call files and directories anything you want. Capital letters, spaces are all ok! ### (Optional) Move a File into the Directory 1. In the File navigation sidebar, hover over the file in the sidebar to show the context menu. From the menu select "Move". 2. Choose the destination directory
![]()
### Create a new Tool file Navigate to the homepage or sidebar and click the 'New File' button. ### Choose the JSON Schema Tool type From the available options, select **Json Schema** as the Tool type. ### Give the tool a name Form the top navigation bar, click on the Tool name, and rename to "Weather tool"
### Define your tool's structure Paste the following JSON into the provided dialog to define your tool's structure: ```json { "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "name": "Location", "description": "The city and state, e.g. San Francisco, CA" }, "unit": { "type": "string", "name": "Unit", "enum": ["celsius", "fahrenheit"] } }, "required": ["location"] } } ``` If you choose to edit or create your own tool, you'll need to use the universal [JSON Schema syntax](https://json-schema.org/). When creating a custom tool, it should correspond to a function you have defined in your own code. The JSON Schema you define here specifies the parameters and structure you want the AI model to use when interacting with your function.
### Commit this version of the Tool Press the **Commit** button to commit this version of the Tool, and set it as the default version by deploying it.
### Navigate to the **Editor** of a Prompt Switch to a model that supports tool calling, such as `gpt-4o`.
To view the list of models that support Tool calling, see the [Models page](/docs/reference/models#models). ### **Add Tool** to the Prompt definition. In the dropdown, go to the **Link existing tool** option. You should see your `Weather Tool` tool, click on it to link it to your editor.### Test that the Prompt is working with the tool Now that your Tool is linked you can start using it. In the **Chat** section, in the **User** input, enter `"what is the weather in london?"` Press the **Run** button. You should see the **Assistant** respond with the tool response and a new **Tool** field inserted to allow you to insert an answer. In this case, put in `22` into the tool response and press **Run**.
The model will respond with `The current weather in London is 22 degrees`. ### Commit the Prompt You've linked a Tool to your Prompt, now let's save it. Press the **Save** button and name your Prompt `weather-model`. ### (Optional) Update the Tool Now that's we've linked your `get_current_weather` tool to your Prompt, let's try updating the base tool and see how it propagates the changes down into your saved `weather-model` version. Navigate back to the Tool in the sidebar and go to the Editor. ### Update the Tool Let's update both the name, as well as the required fields. For the name, update it to `get_current_weather_updated` and for the required fields, add `unit` as a required field. The should look like this now: ```json { "name": "get_current_weather_updated", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "name": "Location", "description": "The city and state, e.g. San Francisco, CA" }, "unit": { "type": "string", "name": "Unit", "enum": ["celsius", "fahrenheit"] } }, "required": ["location", "unit"] } } ```
### Commit and deploy the Tool Press the **Commit** button and then follow the steps to deloy this version of the Tool. Your Tool is now updated. ### Try the Prompt again Navigate back to your previous project, and open the editor. You should see the `weather-model` loaded as the active version. You should also be able to see the name of your previously linked tool in the Tools section now says `get_current_weather_updated`. In the Chat section enter in again, `What is the weather in london?`, and press **Run** again. ### Check the response You should see the updated tool response, and how it now contains the `unit` field. Congratulations, you've successfully linked a JSON Schema tool to your Prompt.
![]()
When updating your Tool, remember that the change will affect all the Prompts that link to it. Be careful when making updates to not inadvertently change something you didn't intend. # Monitor production Logs > Learn how to create and use online Evaluators to observe the performance of your Prompts. [Evaluators](../../explanation/evaluators#online-monitoring) on Humanloop enable you to continuously measure the performance of your Prompts in production. Attach online Evaluators to your Prompts, and Humanloop will automatically run them on new Logs. You can then track the performance of your Prompts over time. ## Prerequisites * You have a Prompt receiving Logs. If not, please follow our [Prompt creation](/docs/development/guides/create-prompt) guide first. * You have an [online Evaluator](../../explanation/evaluators#online-monitoring). The example "Factuality" Evaluator is an online Evaluator that comes pre-configured with your organization. ## Attach Evaluator to your Prompt Attach the online Evaluator to your Prompt. Humanloop will automatically run the Evaluator on new Logs generated by your Prompt.### Open the Prompt's monitoring dialog Go to your Prompt's dashboard. Click **Monitoring** in the top right to open the monitoring dialog.  ### Connect your Evaluator Click **Connect Evaluators** and select the Evaluator you created. ### Ensure "Auto-run" is enabled Ensure the "Auto-run" switch is enabled for the Evaluator. This will ensure the Evaluator runs automatically on new Logs. ## View Evaluator results Humanloop will run the Evaluators on the new Logs as they are generated. ### Graphs over time Evaluator results are summarized in the **Dashboard** tab. Here, Humanloop displays the average Evaluator results over time. You can toggle the period and time resolution covered by the graphs to see how your Prompt has performed over time.  ### Filtering Logs To investigate specific Logs, go to the **Logs** tab. Here, you can see the Evaluator results for each Log generated by your Prompt. The Evaluator you attached above will have a column in the Logs table showing the results of the Evaluator.  You can filter the Logs table by the Evaluator results. Click on the column header to sort the table by the Evaluator results. For Evaluators with options (i.e. those with a return type of `select` or `multi_select`), you can filter the table by the applied options. ## Next steps * Iterate on your Prompt based on the Evaluator results. You can open specific Logs in the Editor to tweak and test new Prompt versions. * [Add Logs of interest to a Dataset](../evals/create-dataset-from-logs) to use in an Evaluation. # Capture user feedback > Learn how to record user feedback on your generated Prompt Logs using the Humanloop SDK. ### Prerequisites * You already have a Prompt — if not, please follow our [Prompt creation](../../guides/prompt/create-prompt) guide first. * You have created a Human Evaluator. For this guide, we will use the "rating" example Evaluator automatically created for your organization.First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: ## Configure feedback To collect user feedback, connect a Human Evaluator to your Prompt. The Evaluator specifies the type of the feedback you want to collect. See our guide on [creating Human Evaluators](../evals/human-evaluators) for more information.```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` You can use the example "rating" Evaluator that is automatically for you. This Evaluator allows users to apply a label of "good" or "bad", and is automatically connected to all new Prompts. If you choose to use this Evaluator, you can skip to the "Log feedback" section. Different use-cases and user interfaces may require different kinds of feedback that need to be mapped to the appropriate end user interaction. There are broadly 3 important kinds of feedback: 1. **Explicit feedback**: these are purposeful actions to review the generations. For example, ‘thumbs up/down’ button presses. 2. **Implicit feedback**: indirect actions taken by your users may signal whether the generation was good or bad, for example, whether the user ‘copied’ the generation, ‘saved it’ or ‘dismissed it’ (which is negative feedback). 3. **Free-form feedback**: Corrections and explanations provided by the end-user on the generation. You should create Human Evaluators structured to capture the feedback you need. For example, a Human Evaluator with return type "text" can be used to capture free-form feedback, while a Human Evaluator with return type "multi\_select" can be used to capture user actions that provide implicit feedback. If you have not done so, you can follow our guide to [create a Human Evaluator](/docs/evaluation/guides/human-evaluator) to set up the appropriate feedback schema. ### Open the Prompt's monitoring dialog Go to your Prompt's dashboard. Click **Monitoring** in the top right to open the monitoring dialog.  ### Connect your Evaluator Click **Connect Evaluators** and select the Human Evaluator you created.  You should now see the selected Human Evaluator attached to the Prompt in the Monitoring dialog.  ## Log feedback With the Human Evaluator attached to your Prompt, you can record feedback against the Prompt's Logs.### Retrieve Log ID The ID of the Prompt Log can be found in the response of the `humanloop.prompts.call(...)` method. ```python log = humanloop.prompts.call( version_id="prv_qNeXZp9P6T7kdnMIBHIOV", path="persona", messages=[{"role": "user", "content": "What really happened at Roswell?"}], inputs={"person": "Trump"}, ) log_id = log.id ``` ### Log the feedback Call `humanloop.evaluators.log(...)` referencing the above Log ID as `parent_id` to record user feedback. ```python feedback = humanloop.evaluators.log( # Pass the `log_id` from the previous step to indicate the Log to record feedback against parent_id=log_id, # Here, we're recording feedback against a "Tweet Issues" Human Evaluator, # which is of type `multi_select` and has multiple options to choose from. path="Feedback Demo/Tweet Issues", judgment=["Inappropriate", "Too many emojis"], ) ``` ## View feedback You can view the applied feedback in two main ways: through the Logs that the feedback was applied to, and through the Evaluator itself. ### Feedback applied to Logs The feedback recorded for each Log can be viewed in the **Logs** table of your Prompt.  Your internal users can also apply feedback to the Logs directly through the Humanloop app.  ### Feedback for an Evaluator You can view all feedback recorded for a specific Human Evaluator in the **Logs** tab of the Evaluator. This will display all feedback recorded for the Evaluator across all other Files.  ## Next steps * [Create and customize your own Human Evaluators](../evals/human-evaluators) to capture the feedback you need. * Human Evaluators can also be used in Evaluations, allowing you to [collect judgments from your subject-matter experts](../evals/run-human-evaluation). # Logging through API > Add logging your AI project using the Humanloop API. Our SDK offers high-level utilities for integrating Humanloop in your project. You can use the API to the same effect with any language you use or if you prefer more control. This guide revisits our [logging quickstart tutorial](/docs/v5/quickstart/set-up-logging): we'll use API actions instead of the SDK decorators, showing you how Humanloop instrumentation works step-by-step. By the end, we'll have a chat agent project integrated with Humanloop logging. The example uses the Python SDK, but the verbs map directly [to our API](/docs/v5/api-reference/sdks). ## PrerequisitesThe "rating" and "correction" Evaluators are attached to all Prompts by default. You can record feedback using these Evaluators as well. The "rating" Evaluator can be used to record explicit feedback (e.g. from a 👍/👎 button). ```python rating_log = humanloop.evaluators.log( parent_id=log_id, # We're recording feedback using the "rating" Human Evaluator, # which has 2 options: "good" and "bad". path="rating", judgment="good", # You can also include the source of the feedback when recording it with the `user` parameter. user="user_123", ) ``` The "correction" Evaluator can be used to record user-provided corrections to the generations (e.g. If the user edits the generation before copying it). ```python correction_log = humanloop.evaluators.log( parent_id=log_id, path="correction", judgment="NOTHING happened at Roswell, folks! Fake News media pushing ALIEN conspiracy theories. SAD! " + "I know Area 51, have the best aliens. Roswell? Total hoax! Believe me. 👽🚫 #Roswell #FakeNews", ) ``` If the user removes their feedback (e.g. if the user deselects a previous 👎 feedback), you can record this by passing `judgment=None`. ```python removed_rating_log = humanloop.evaluators.log( parent_id=log_id, path="rating", judgment=None, ) ``` ## Create the chat agent We start with a simple chat agent that answers math and science questions. Create a Humanloop Account
If you haven't already, [create an account](https://app.humanloop.com/signup) or [log in](https://app.humanloop.com/login) to HumanloopAdd an OpenAI API Key
If you're the first person in your organization, you'll need to add an API key to a model provider. 1. Go to OpenAI and [grab an API key](https://platform.openai.com/api-keys). 2. In Humanloop [Organization Settings](https://app.humanloop.com/account/api-keys) set up OpenAI as a model provider.Using the Prompt Editor will use your OpenAI credits in the same way that the OpenAI playground does. Keep your API keys for Humanloop and the model providers private. ```bash pip install humanloop openai ``` Humanloop SDK requires Python 3.9 or higher. Optionally, create a virtual environment to keep dependencies tidy. {/* TODO: Add a disclaimer for TS */} ## Log to Humanloop The agent works and is capable of function calling. However, we rely on inputs and outputs to reason about the behavior. Humanloop logging allows you to observe the steps taken by the agent, which we will demonstrate below. Create an `agent.py` file and add the following: ```python title="agent.py" maxLines=100 import os import json import datetime from humanloop import Humanloop from openai import OpenAI openai = OpenAI(api_key="YOUR_OPENAI_KEY") humanloop = Humanloop(api_key="YOUR_HUMANLOOP_KEY") def calculator(operation: str, num1: int, num2: int) -> str: """Do arithmetic operations on two numbers.""" if operation == "add": return num1 + num2 elif operation == "subtract": return num1 - num2 elif operation == "multiply": return num1 * num2 elif operation == "divide": return num1 / num2 else: return "Invalid operation" TOOL_JSON_SCHEMA = { "name": "calculator", "description": "Do arithmetic operations on two numbers.", "parameters": { "type": "object", "required": ["operation", "num1", "num2"], "properties": { "operation": {"type": "string"}, "num1": {"type": "integer"}, "num2": {"type": "integer"}, }, "additionalProperties": False, }, } def call_model(messages: list[str]) -> str: output = openai.chat.completions.create( messages=messages, model="gpt-4o", tools=[ { "type": "function", "function": TOOL_JSON_SCHEMA, } ], temperature=0.7, ) # Check if model asked for a tool call if output.choices[0].message.tool_calls: for tool_call in output.choices[0].message.tool_calls: arguments = json.loads(tool_call.function.arguments) if tool_call.function.name == "calculator": result = calculator(**arguments) return f"[TOOL CALL] {result}" # Otherwise, return the LLM response return output.choices[0].message.content def conversation(): messages = [ { "role": "system", "content": "You are a a groovy 80s surfer dude " "helping with math and science.", }, ] while True: user_input = input("You: ") if user_input == "exit": break messages.append({"role": "user", "content": user_input}) response = call_model(messages=messages) messages.append({"role": "assistant", "content": response}) print(f"Agent: {response}") if __name__ == "__main__": conversation() ``` Create an `agent.ts` file and add the following: ```typescript title="agent.ts" maxLines=100 import { OpenAI } from "openai"; import * as readline from 'readline/promises'; import { HumanloopClient } from "humanloop"; import { ChatCompletionMessageParam } from "openai/resources"; const openai = new OpenAI({ apiKey: "YOUR_OPENAI_KEY" }); const humanloop = new HumanloopClient({apiKey: "YOUR_HUMANLOOP_KEY"}); function calculator(operation: string, num1: number, num2: number): string | number { /** Do arithmetic operations on two numbers. */ switch (operation) { case "add": return num1 + num2; case "subtract": return num1 - num2; case "multiply": return num1 * num2; case "divide": return num1 / num2; default: return "Invalid operation"; } } const TOOL_JSON_SCHEMA = { name: "calculator", description: "Do arithmetic operations on two numbers.", parameters: { type: "object", required: ["operation", "num1", "num2"], properties: { operation: { type: "string" }, num1: { type: "integer" }, num2: { type: "integer" }, }, additionalProperties: false, }, }; async function callModel(messages: ChatCompletionMessageParam[]): Promise { const output = await openai.chat.completions.create({ messages: messages, model: "gpt-4o", tools: [ { type: "function", function: TOOL_JSON_SCHEMA, }, ], temperature: 0.7, }); // Check if model asked for a tool call const toolCalls = output.choices[0]?.message?.tool_calls; if (toolCalls) { for (const toolCall of toolCalls) { const toolArguments = JSON.parse(toolCall.function.arguments); if (toolCall.function.name === "calculator") { const toolStartTime = new Date(); const result = calculator(toolArguments.operation, toolArguments.num1, toolArguments.num2); return `[TOOL CALL] ${result}`; } } } // Otherwise, return the LLM response return output.choices[0]?.message?.content || ""; } async function conversation() { const messages: ChatCompletionMessageParam[] = [ { role: "system", content: "You are a groovy 80s surfer dude helping with math and science.", }, ]; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); while (true) { let userInput = await rl.question("You: ") if (userInput === "exit") { rl.close(); break; } messages.push({ role: "user", content: userInput }); const response = await callModel(traceId, messages); messages.push({ role: "assistant", content: response }); console.log(`Agent: ${response}`); } } conversation(); ``` ### Initialize the trace Modify `call_model` to accept a `trace_id` argument. It will be used to associate [Logs](/docs/v5/explanations/logs) to the logging trace. The trace of the conversation will be associated with a [Flow](/docs/v5/explanations/flows). Initialize the trace at the start of the conversation. ## Run the code Have a conversation with the agent. When you're done, type `exit` to close the program.### Add logging We add log statements that will create the Logs contained in the trace. ```python title="agent.py" highlight={1,5-10,23} maxLines=30 def call_model(trace_id: str, messages: list[str]) -> str: ... def conversation(): trace_id = humanloop.flows.log( path="Logging Quickstart/QA Agent", flow={ "attributes": {}, }, ).id messages = [ { "role": "system", "content": "You are a a groovy 80s surfer dude " "helping with math and science.", }, ] while True: user_input = input("You: ") if user_input == "exit": break messages.append({"role": "user", "content": user_input}) response = call_model(trace_id=trace_id, messages=messages) messages.append({"role": "assistant", "content": response}) print(f"Agent: {response}") ``` ```typescript title="agent.ts" highlight={1,6-13,35} maxLines=30 async function callModel(traceId: string, messages: ChatCompletionMessageParam[]): Promise { ... } async function conversation() { const flowLog = await humanloop.flows.log({ path="Logging Quickstart/QA Agent", flow={ "attributes": {}, }, }); const traceId = flowLog.id; const messages: ChatCompletionMessageParam[] = [ { role: "system", content: "You are a groovy 80s surfer dude helping with math and science.", }, ]; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); while (true) { let userInput = await rl.question("You: ") if (userInput === "exit") { rl.close(); break; } messages.push({ role: "user", content: userInput }); const response = await callModel(traceId, messages); messages.push({ role: "assistant", content: response }); console.log(`Agent: ${response}`); } } ``` ### Complete the trace When the conversation is finished, we mark the trace as complete, signalling no more logs will be added. ```python title="agent.py" highlight={2,12-23,30,32-44} def call_model(trace_id: str, messages: list[str]) -> str: prompt_start_time = datetime.datetime.now() output = openai.chat.completions.create( messages=messages, model="gpt-4o", tools=[{ "type": "function", "function": TOOL_JSON_SCHEMA, }], temperature=0.7, ) prompt_log_id = humanloop.prompts.log( path="Logging Quickstart/QA Prompt", prompt={ "model": "gpt-4o", "tools": [TOOL_JSON_SCHEMA], "temperature": 0.7, }, output=output.choices[0].message.content, trace_parent_id=trace_id, start_time=prompt_start_time, end_time=datetime.datetime.now(), ).id # Check if model asked for a tool call if output.choices[0].message.tool_calls: for tool_call in output.choices[0].message.tool_calls: arguments = json.loads(tool_call.function.arguments) if tool_call.function.name == "calculator": tool_start_time = datetime.datetime.now() result = calculator(**arguments) humanloop.tools.log( path="Logging Quickstart/Calculator", tool={ "name": "calculator", "description": "Do arithmetic operations on two numbers.", "function": TOOL_JSON_SCHEMA, }, inputs=arguments, output=result, trace_parent_id=prompt_log_id, start_time=tool_start_time, end_time=datetime.datetime.now(), ) return f"[TOOL CALL] {result}" # Otherwise, return the LLM response return output.choices[0].message.content ``` ```typescript title="agent.ts" highlight={2,15-28,39-53} async function callModel(traceId: string, messages: ChatCompletionMessageParam[]): Promise { const promptStartTime = new Date(); const output = await openai.chat.completions.create({ messages: messages, model: "gpt-4o", tools: [ { type: "function", function: TOOL_JSON_SCHEMA, }, ], temperature: 0.7, }); const promptLog = await humanloop.prompts.log({ path: "Logging Quickstart/QA Prompt", prompt: { model: "gpt-4o", tools: [TOOL_JSON_SCHEMA], temperature: 0.7, }, output: output.choices[0]?.message?.content || "", traceParentId: traceId, startTime: promptStartTime, endTime: new Date(), }); const promptLogId = promptLog.id; // Check if model asked for a tool call const toolCalls = output.choices[0]?.message?.tool_calls; if (toolCalls) { for (const toolCall of toolCalls) { const toolArguments = JSON.parse(toolCall.function.arguments); if (toolCall.function.name === "calculator") { const toolStartTime = new Date(); const result = calculator(toolArguments.operation, toolArguments.num1, toolArguments.num2); await humanloop.tools.log({ path: "Logging Quickstart/Calculator", tool: { "function": { "name": "calculator", "description": "Do arithmetic operations on two numbers.", "parameters": TOOL_JSON_SCHEMA }, }, inputs: toolArguments, output: JSON.stringify(result), traceParentId: promptLogId, startTime: toolStartTime, endTime: new Date(), }); return `[TOOL CALL] ${result}`; } } } // Otherwise, return the LLM response return output.choices[0]?.message?.content || ""; } async function conversation() { const traceLog = await humanloop.flows.log({ path: "Logging Quickstart/QA Agent", flow: { attributes: {}, }, }); const traceId = traceLog.id; const messages: ChatCompletionMessageParam[] = [ { role: "system", content: "You are a groovy 80s surfer dude helping with math and science.", }, ]; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); while (true) { let userInput = await rl.question("You: ") if (userInput === "exit") { rl.close(); break; } messages.push({ role: "user", content: userInput }); const response = await callModel(traceId, messages); messages.push({ role: "assistant", content: response }); console.log(`Agent: ${response}`); } await humanloop.flows.updateLog(traceId, { "output": "", "traceStatus": "complete", }); } ``` ```python title="agent.py" highlight={24-28} maxLines=30 def conversation(): trace_id = humanloop.flows.log( path="Logging Quickstart/QA Agent", flow={ "attributes": {}, }, ).id messages = [ { "role": "system", "content": "You are a a groovy 80s surfer dude " "helping with math and science.", }, ] while True: user_input = input("You: ") if user_input == "exit": break messages.append({"role": "user", "content": user_input}) response = call_model(trace_id=trace_id, messages=messages) messages.append({"role": "assistant", "content": response}) print(f"Agent: {response}") humanloop.flows.update_log( log_id=trace_id, output="", status="complete", ) ``` ```typescript title="agent.ts" highlight={36-39} async function conversation() { const traceLog = await humanloop.flows.log({ path: "Logging Quickstart/QA Agent", flow: { attributes: {}, }, }); const traceId = traceLog.id; const messages: ChatCompletionMessageParam[] = [ { role: "system", content: "You are a groovy 80s surfer dude helping with math and science.", }, ]; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); while (true) { let userInput = await rl.question("You: ") if (userInput === "exit") { rl.close(); break; } messages.push({ role: "user", content: userInput }); const response = await callModel(traceId, messages); messages.push({ role: "assistant", content: response }); console.log(`Agent: ${response}`); } await humanloop.flows.updateLog(traceId, { "output": "", "traceStatus": "complete", }); } ``` ## Check your workspace Navigate to [your workspace](https://app.humanloop.com) to see the logged conversation. Inside the **Logging Quickstart** directory on the left, click the **QA Agent** [Flow](/docs/v5/explanation/flows). Select the **Logs** tab from the top of the page and click the Log inside the table. You will see the conversation's trace, containing Logs corresponding to the [Tool](/docs/v5/explanation/tools) and the [Prompt](/docs/v5/explanation/prompts). ```bash curl title="Terminal" python agent.py You: Hi dude! Agent: Tubular! I am here to help with math and science, what is groovin? You: How does flying work? Agent: ... You: What is 5678 * 456? Agent: [TOOL CALL] 2587968 You: exit ``` ```bash curl title="Terminal" npx tsc && node dist/index.js You: Hi dude! Agent: Tubular! I am here to help with math and science, what is groovin? You: How does flying work? Agent: ... You: What is 5678 * 456? Agent: [TOOL CALL] 2587968 You: exit ``` ## Change the agent and rerun Modify the `call_model` function to use a different model and temperature.
Run the agent again, then head back to your workspace. Click the **QA Prompt** [Prompt](/docs/v5/explanation/prompts), select the **Dashboard** tab from the top of the page and look at **Uncommitted** Versions. By changing the hyperparameters of the OpenAI call, you have tagged a new version of the Prompt. ```python title="agent.py" highlight={5,10,15,17} maxLines=30 def call_model(trace_id: str, messages: list[str]) -> str: prompt_start_time = datetime.datetime.now() output = openai.chat.completions.create( messages=messages, model="gpt-4o-mini", tools=[{ "type": "function", **TOOL_JSON_SCHEMA, }], temperature=0.2, ) prompt_log_id = humanloop.prompts.log( path="Logging Quickstart/QA Prompt", prompt={ "model": "gpt-4o-mini", "tools": [TOOL_JSON_SCHEMA], "temperature": 0.2, } output=output.choices[0].message.content, trace_parent_id=trace_id, start_time=prompt_start_time, end_time=datetime.datetime.now(), ).id ... ``` ```typescript title="agent.ts" highlight={5,12,18,20} maxLines=30 async function callModel(traceId: string, messages: ChatCompletionMessageParam[]): Promise { const promptStartTime = new Date(); const output = await openai.chat.completions.create({ messages: messages, model: "gpt-4o-mini", tools: [ { type: "function", function: TOOL_JSON_SCHEMA, }, ], temperature: 0.2, }); const promptLog = await humanloop.prompts.log({ path: "Logging Quickstart/QA Prompt", prompt: { model: "gpt-4o-mini", tools: [TOOL_JSON_SCHEMA], temperature: 0.2, }, output: output.choices[0]?.message?.content || "", traceParentId: traceId, startTime: promptStartTime, endTime: new Date(), }); ... ``` ## Next steps Logging is the first step to observing your AI product. Read these guides to learn more about evals on Humanloop: * Add [monitoring Evaluators](/docs/v5/guides/observability/monitoring) to evaluate Logs as they're made against a File. * See evals in action in our [tutorial on evaluating an agent](/docs/v5/tutorials/agent-evaluation). # Invite collaborators > Inviting people to your organization allows them to interact with your Humanloop projects. Inviting people to your organization allows them to interact with your Humanloop workspace: * Teammates will be able to create new Prompts, Tools, Datasets and Evaluators. * Developers will be able to get an API key to call, log and evaluate the Prompts via the SDK. * Annotators may give feedback the Logs and provide the human reviews for Evaluations. ## Invite Users To invite users to your organization:
### Go to your organization's **[Members page](https://app.humanloop.com/account/members)** ### Enter the **email address** Enter the email of the person you wish to invite into the **Invite members** box. 🎉 Once they create an account, they can view your projects at the same URL to begin collaborating. # Manage API keys > How to create, share and manage you Humanloop API keys. The API keys allow you to access the Humanloop API programmatically in your app. ## Create a new API key### Click **Send invite**. An email will be sent to the entered email address, inviting them to the organization. If the entered email address is not already a Humanloop user, they will be prompted to create an account before being added to the organization.
### Go to your Organization's **[API Keys page](https://app.humanloop.com/account/api-keys)**. ### Click the **Create new API key** button. ### Enter a name for your API key. Choose a name that helps you identify the key's purpose. You can't change the name of an API key after it's created. ### Click **Create**. ## Revoke an API key You can revoke an existing API key if it is no longer needed.### Copy the generated API key Save it in a secure location. You will not be shown the full API key again.
![]()
When an API key is revoked, future API requests that use this key will be rejected. Any systems that are dependent on this key will no longer work. ### Go to API keys page Go to your Organization's **[API Keys page](https://app.humanloop.com/account/api-keys)**. ### Identify the API key Find the key you wish to revoke by its name or by the displayed trailing characters. ### Click 'Revoke' Click the three dots button on the right of its row to open its menu. Click **Revoke**. A confirmation dialog will be displayed. Click **Remove**. # Manage Environments > Environments are a tagging system for deploying Prompts. They enable you to deploy maintain a streamlined deployment workflow and keep track of different versions of Prompts. [Environments](/docs/explanation/environments) are a tagging system for deploying Prompts. They enable you to deploy maintain a streamlined deployment workflow and keep track of different versions of Prompts. The default environment is your production environment. Everytime you fetch a Prompt, Tool, Dataset etc. without specifying an alternative environment or specific version, the version that is tagged with the default environment is returned. ## Create an environment![]()
### Go to your [Environments](https://app.humanloop.com/account/environments) tab in your Organization's settings. ### Click the '**+ Environment**' button to open the new environment dialog ### Assign a custom name to the environment We recommend something short. For example, you could use `staging`, `prod`, `qa`, `dev`, `testing`, etc. This name is be used to identify the environment in the UI and in the API. ### Click **Create**. ## Rename an environment You can rename an environment to re-arrange your development workflows. Since each new file is automatically deployed to the default environment, which is production unless altered, it may make more sense to create a separate production environment and rename your current environments.
Renaming the environments will take immediate effect, so ensure that this change is planned and does not disrupt your production workflows. ### Go to environments page Go to your Organization's **[environments page](https://app.humanloop.com/account/environments)**. ### Identify the environment Find the environment you wish to rename. ### Click 'Rename' Click the three dots button on the right of its row to open its menu. Click **Rename**. A confirmation dialog will be displayed. Update the name and click **Rename**. # Deployment Options > Humanloop is SOC-2 compliant, offers within your VPC and never trains on your data. Learn more about our hosting options. Humanloop offers a broad range of hosting environments to meet the security and compliance needs of enterprise customers. Our menu of hosting options is as follows from basic to more advanced: 1. **Default**: Our multi-tenanted cloud offering is SOC2 compliant and hosted in AWS US-east region on AWS. 2. **Region specific**: Same as 1, but where additional region requirements for data storage are required - e.g. data can never leave the EU for GDPR reasons. We offer UK, EU and US guarantees for data storage regions. 3. **Dedicated**: We provision your own dedicated instance of Humanloop in your region of choice. With the additional added benefits: * Full [HIPAA compliant](https://aws.amazon.com/compliance/hipaa-compliance/) AWS setup. * Ability to manage your own encryption keys in KMS. * Ability to subscribe to application logging and cloudtrail infrastructure monitoring. 4. **Self-hosted**: You deploy an instance of Humanloop within your own VPC on AWS. We provide an infra as code setup with [Pulumi](https://www.pulumi.com/) to easily spin up a Humanloop instance in your VPC. # Supported Models > Humanloop supports all the major large language model providers, including OpenAI, Anthropic, Google, AWS Bedrock, Azure, and more. Additionally, you can use your own custom models with with the API and still benefit from the Humanloop platform. Humanloop supports all the major large language model providers, including OpenAI, Anthropic, Google, AWS Bedrock, Azure, and more. Additionally, you can use your own custom models with with the API and still benefit from the Humanloop platform. ## Providers Here is a summary of which providers are supported, and what information is available for each provider automatically. | Provider | Models | Cost information | Token information | | ----------- | ---------------- | ---------------- | ----------------- | | OpenAI | ✅ | ✅ | ✅ | | Anthropic | ✅ | ✅ | ✅ | | Google | ✅ | ✅ | ✅ | | Azure | ✅ | ✅ | ✅ | | Cohere | ✅ | ✅ | ✅ | | Llama | ✅ | | | | Groq | ✅ | | | | AWS Bedrock | Anthropic, Llama | ✅ | ✅ | Adding in more providers is driven by customer demand. If you have a specific provider or model you would like to see supported, please reach out to us at [support@humanloop.com](mailto:support@humanloop.com). ## Models The following are models that are integrated with Humanloop. This means that they can be used in the Prompt Editor and are callable through the Humanloop API. If you have a specific model you would like to see supported, please reach out to us at [support@humanloop.com](mailto:support@humanloop.com).![]()
Remember, you can always use any model you want including your own self-hosted models, if you orchestrate the API calls yourself and log the data to Humanloop. | Provider | Model | Max Prompt Tokens | Max Output Tokens | Cost per Prompt Token | Cost per Output Token | Tool Support | Image Support | | ------------- | --------------------------- | ----------------- | ----------------- | --------------------- | --------------------- | ------------ | ------------- | | openai | o1-preview | 128000 | 32768 | \$0.000015 | \$0.00006 | ❌ | ❌ | | openai | o1-mini | 128000 | 65536 | \$0.000003 | \$0.000012 | ❌ | ❌ | | openai | gpt-4o-64k-output-alpha | 128000 | 64000 | \$0.000006 | \$0.000018 | ✅ | ✅ | | openai | gpt-4o | 128000 | 4096 | \$0.000005 | \$0.000015 | ✅ | ✅ | | openai | gpt-4o-mini | 128000 | 4096 | \$0.00000015 | \$0.0000006 | ✅ | ✅ | | openai | gpt-4 | 8192 | 4096 | \$0.00003 | \$0.00006 | ✅ | ❌ | | openai | gpt-4-turbo | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ✅ | | openai | gpt-4-turbo-2024-04-09 | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai | gpt-4-32k | 32768 | 4096 | \$0.00003 | \$0.00003 | ✅ | ❌ | | openai | gpt-4-1106-preview | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai | gpt-4-0125-preview | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai | gpt-4-vision | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ✅ | | openai | gpt-4-1106-vision-preview | 16385 | 4096 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai | gpt-3.5-turbo | 16385 | 4096 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai | gpt-3.5-turbo-instruct | 8192 | 4097 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai | babbage-002 | 16384 | 16384 | \$0.0000004 | \$0.0000004 | ✅ | ❌ | | openai | davinci-002 | 16384 | 16384 | \$0.000002 | \$0.000002 | ✅ | ❌ | | openai | ft:gpt-3.5-turbo | 4097 | 4096 | \$0.000003 | \$0.000006 | ✅ | ❌ | | openai | ft:davinci-002 | 16384 | 16384 | \$0.000002 | \$0.000002 | ✅ | ❌ | | openai | text-moderation | 32768 | 32768 | \$0.000003 | \$0.000004 | ✅ | ❌ | | anthropic | claude-3-5-sonnet-20241022 | 200000 | 8192 | \$0.000003 | \$0.000015 | ✅ | ✅ | | anthropic | claude-3-5-sonnet-20240620 | 200000 | 4096 | \$0.000003 | \$0.000015 | ✅ | ✅ | | anthropic | claude-3-5-haiku-20241022 | 200000 | 8192 | \$0.000003 | \$0.000015 | ✅ | ✅ | | anthropic | claude-3-opus-20240229 | 200000 | 4096 | \$0.000015 | \$0.000075 | ✅ | ❌ | | anthropic | claude-3-sonnet-20240229 | 200000 | 4096 | \$0.000003 | \$0.000015 | ✅ | ❌ | | anthropic | claude-3-haiku-20240307 | 200000 | 4096 | \$0.00000025 | \$0.00000125 | ✅ | ❌ | | anthropic | claude-2.1 | 100000 | 4096 | \$0.00000025 | \$0.000024 | ❌ | ❌ | | anthropic | claude-2 | 100000 | 4096 | \$0.000008 | \$0.000024 | ❌ | ❌ | | google | gemini-pro-vision | 16384 | 2048 | \$0.00000025 | \$0.0000005 | ❌ | ✅ | | google | gemini-1.0-pro-vision | 16384 | 2048 | \$0.00000025 | \$0.0000005 | ❌ | ✅ | | google | gemini-pro | 32760 | 8192 | \$0.00000025 | \$0.0000005 | ❌ | ❌ | | google | gemini-1.0-pro | 32760 | 8192 | \$0.00000025 | \$0.0000005 | ❌ | ❌ | | google | gemini-1.5-pro-latest | 1000000 | 8192 | \$0.00000025 | \$0.0000005 | ❌ | ❌ | | google | gemini-1.5-pro | 1000000 | 8192 | \$0.00000025 | \$0.0000005 | ❌ | ❌ | | google | gemini-1.5-flash | 1000000 | 8192 | \$0.000000075 | \$0.0000003 | ✅ | ✅ | | google | gemini-1.5-flash-8b | 1000000 | 8192 | \$0.0000000375 | \$0.00000015 | ✅ | ✅ | | openai\_azure | o1-preview | 128000 | 32768 | \$0.000015 | \$0.00006 | ❌ | ❌ | | openai\_azure | o1-mini | 128000 | 65536 | \$0.000003 | \$0.000012 | ❌ | ❌ | | openai\_azure | gpt-4o | 128000 | 4096 | \$0.000005 | \$0.000015 | ✅ | ✅ | | openai\_azure | gpt-4o-2024-05-13 | 128000 | 4096 | \$0.000005 | \$0.000015 | ✅ | ✅ | | openai\_azure | gpt-4-turbo-2024-04-09 | 128000 | 4096 | \$0.00003 | \$0.00006 | ✅ | ✅ | | openai\_azure | gpt-4 | 8192 | 4096 | \$0.00003 | \$0.00006 | ✅ | ❌ | | openai\_azure | gpt-4-0314 | 8192 | 4096 | \$0.00003 | \$0.00006 | ✅ | ❌ | | openai\_azure | gpt-4-32k | 32768 | 4096 | \$0.00006 | \$0.00012 | ✅ | ❌ | | openai\_azure | gpt-4-0125 | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai\_azure | gpt-4-1106 | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai\_azure | gpt-4-0613 | 8192 | 4096 | \$0.00003 | \$0.00006 | ✅ | ❌ | | openai\_azure | gpt-4-turbo | 128000 | 4096 | \$0.00001 | \$0.00003 | ✅ | ❌ | | openai\_azure | gpt-4-turbo-vision | 128000 | 4096 | \$0.000003 | \$0.000004 | ✅ | ✅ | | openai\_azure | gpt-4-vision | 128000 | 4096 | \$0.000003 | \$0.000004 | ✅ | ✅ | | openai\_azure | gpt-35-turbo-1106 | 16384 | 4096 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai\_azure | gpt-35-turbo-0125 | 16384 | 4096 | \$0.0000005 | \$0.0000015 | ✅ | ❌ | | openai\_azure | gpt-35-turbo-16k | 16384 | 4096 | \$0.000003 | \$0.000004 | ✅ | ❌ | | openai\_azure | gpt-35-turbo | 4097 | 4096 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai\_azure | gpt-3.5-turbo-instruct | 4097 | 4096 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | openai\_azure | gpt-35-turbo-instruct | 4097 | 4097 | \$0.0000015 | \$0.000002 | ✅ | ❌ | | cohere | command-r | 128000 | 4000 | \$0.0000005 | \$0.0000015 | ❌ | ❌ | | cohere | command-light | 4096 | 4096 | \$0.000015 | \$0.000015 | ❌ | ❌ | | cohere | command-r-plus | 128000 | 4000 | \$0.000003 | \$0.000015 | ❌ | ❌ | | cohere | command-nightly | 4096 | 4096 | \$0.000015 | \$0.000015 | ❌ | ❌ | | cohere | command | 4096 | 4096 | \$0.000015 | \$0.000015 | ❌ | ❌ | | cohere | command-medium-beta | 4096 | 4096 | \$0.000015 | \$0.000015 | ❌ | ❌ | | cohere | command-xlarge-beta | 4096 | 4096 | \$0.000015 | \$0.000015 | ❌ | ❌ | | groq | mixtral-8x7b-32768 | 32768 | 32768 | \$0.0 | \$0.0 | ❌ | ❌ | | groq | llama-3.2-1b-preview | 131072 | 8192 | \$0.0 | \$0.0 | ✅ | ❌ | | groq | llama-3.2-3b-preview | 131072 | 8192 | \$0.0 | \$0.0 | ✅ | ❌ | | groq | llama3-1-70b-versatile | 131072 | 8192 | \$0.0 | \$0.0 | ✅ | ❌ | | groq | llama3-1-8b-instant | 131072 | 8192 | \$0.0 | \$0.0 | ✅ | ❌ | | groq | llama3-8b-8192 | 8192 | 8192 | \$0.0 | \$0.0 | ❌ | ❌ | | groq | llama3-70b-8192 | 8192 | 8192 | \$0.0 | \$0.0 | ❌ | ❌ | | groq | gemma2-9b-it | 8192 | 8192 | \$0.0 | \$0.0 | ❌ | ❌ | | groq | gemma-7b-it | 8192 | 8192 | \$0.0 | \$0.0 | ❌ | ❌ | | replicate | llama-3-70b-instruct | 8192 | 8192 | \$0.00000065 | \$0.00000275 | ❌ | ❌ | | replicate | llama-3-70b | 8192 | 8192 | \$0.00000065 | \$0.00000275 | ❌ | ❌ | | replicate | llama-3-8b-instruct | 8192 | 8192 | \$0.00000005 | \$0.00000025 | ❌ | ❌ | | replicate | llama-3-8b | 8192 | 8192 | \$0.00000005 | \$0.00000025 | ❌ | ❌ | | replicate | llama-2-70b | 4096 | 4096 | \$0.00003 | \$0.00006 | ❌ | ❌ | | replicate | llama70b-v2 | 4096 | 4096 | N/A | N/A | ❌ | ❌ | | replicate | mixtral-8x7b | 4096 | 4096 | N/A | N/A | ❌ | ❌ | | google | gemini-1.5-flash | 1048576 | 8192 | \$0.000000075 | \$0.0000003 | ✅ | ✅ | | google | gemini-1.5-pro | 2097152 | 8192 | \$0.0000035 | \$0.0000105 | ✅ | ❌ | | bedrock\* | anthropic.claude-3.5-sonnet | 200000 | 4096 | \$0.000003 | \$0.000015 | ✅ | ✅ | | bedrock\* | anthropic.claude-3-5-haiku | 200000 | 4096 | \$0.000001 | \$0.000005 | ✅ | ✅ | | bedrock\* | anthropic.claude-3-sonnet | 200000 | 4096 | \$0.000003 | \$0.000015 | ✅ | ✅ | | bedrock\* | anthropic.claude-3-haiku | 200000 | 4096 | \$0.00000025 | \$0.00000125 | ✅ | ✅ | | bedrock\* | anthropic.claude-3-opus | 200000 | 4096 | \$0.000015 | \$0.000075 | ✅ | ✅ | | bedrock\* | meta.llama3-1-405b-instruct | 131072 | 2048 | \$0.00000532 | \$0.000016 | ✅ | ❌ | | bedrock\* | meta.llama3-1-70b-instruct | 131072 | 2048 | \$0.00000022 | \$0.00000022 | ✅ | ❌ | | bedrock\* | meta.llama3-1-8b-instruct | 131072 | 2048 | \$0.00000099 | \$0.00000099 | ✅ | ❌ | \* AWS Bedrock prices differ based on region. The prices listed are for us-west-2 # Template Library > Explore Humanloop’s template library. Find example evaluators and prompts for popular use cases like Agents and RAG, all ready for customization.To help you get started, we provide a library of example Evaluators, Prompts, Tools and Datasets. These examples have been designed for common AI use-cases like RAG, customer service, and agents. Each example can be duplicated to your workspace and customized for your use cases. ## Browse the Library The Template Library includes various examples built for common AI application needs, including: #### Evaluators Pre-built evaluators for measuring semantic similarity, cost, latency, and other key metrics. Quickly assess your AI's performance with ready-made evaluators that are suited for RAG. Measure response accuracy, prompt quality, and user satisfaction with tools designed for reliable and consistent evaluation. #### Prompts Example prompts for common use cases including classification, question-answering, content generation, conversational agents, and RAG (Retrieval-Augmented Generation). #### Tools Ready-to-use tools for tasks like retrieval, data extraction, and calling external functions with JSON Schema function calling. #### Datasets Example datasets for common use cases including classification, question-answering, and content generation. #### Example Agents and Flows Example agent templates designed for common use cases like customer support, guided workflows, and RAG applications. These templates demonstrate best practices for building AI agents that can classify queries, answer questions, generate content, and provide context-aware responses by retrieving and incorporating relevant information. ## How to use templates 1. Navigate to the 'Library' page in the sidebar 2. Filter by type, category, or tags 3. Select a template you'd like to use 4. Click "Duplicate" to create a copy in your workspace that you can customize Once you've added a template to your workspace, you can modify it to match your specific requirements. Learn more about customizing your files in our guides for [Evaluators](/docs/evaluation/guides/code-based-evaluator), [Prompts](/docs/development/prompt-engineering), and [Tools](/docs/development/tools). # Vercel AI SDK > Learn about the ways you can use Humanloop with the Vercel AI SDK. ## Observability integration The Vercel AI SDK supports [tracing via OpenTelemetry](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry). You can export these traces to Humanloop by enabling telemetry and configuring the OpenTelemetry Exporter.
The Vercel AI SDK tracing feature is experimental and subject to change. You must enable it with the `experimental_telemetry` parameter on each AI SDK function call that you want to trace. Learn how to add tracing to your AI SDK application below. ### Metadata parameters Humanloop's AI SDK OpenTelemetry Receiver will automatically extract the following metadata parameters from the `experimental_telemetry` metadata object: * `humanloop.directoryPath`: **\[Required]** The path to the directory on Humanloop. Generation spans will create Logs for this Directory on Humanloop. * `humanloop.traceId`: **\[Optional]** The ID of a Flow Log on Humanloop. Set this to group multiple calls to the AI SDK into a single Flow Log on Humanloop. ### Prerequisites The following steps assume you're already using the AI SDK in your application. If not, follow [Vercel's quickstarts](https://sdk.vercel.ai/docs/getting-started) to get started.## Learn more To see the integration in action, check out our [Vercel AI SDK guides](/docs/v5/integrations/vercel-ai-sdk). # Prompt file format > The `.prompt` file format is a human-readable and version-control-friendly format for storing model configurations. Versions of Next \< 15 must set `experimental.instrumentationHook` in `next.config.js`. Learn more [here](https://nextjs.org/docs/app/building-your-application/optimizing/open-telemetry). You can find an example Next.js application that uses the AI SDK to stream chat responses [here](https://sdk.vercel.ai/cookbook/next/stream-text-with-chat-prompt).### Set up OpenTelemetry Install dependencies. ```bash title="npm" wordWrap npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation ``` ```bash title="pnpm" wordWrap pnpm add @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation ``` ```bash title="yarn" wordWrap yarn add @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation ``` Create a file called `instrumentation.ts` in your root or /src directory and add the following: ```typescript title="instrumentation.ts" import { registerOTel } from '@vercel/otel'; export function register() { registerOTel({ serviceName: 'humanloop-vercel-ai-sdk' }); } ``` ### Configure OpenTelemetry Configure the [OpenTelemetry exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) to forward logs to Humanloop. ```plaintext title=".env.local" wordWrap HUMANLOOP_API_KEY=# Configure the OpenTelemetry OTLP Exporter OTEL_EXPORTER_OTLP_ENDPOINT=https://api.humanloop.com/v5/import/otel OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY= " # Humanloop API key ``` ### Trace AI SDK calls Now add the `experimental_telemetry` parameter to your AI SDK function calls to trace them. With a simple one-step generation, each call to `streamText` or `generateText` will be traced as a Prompt Log on Humanloop. ```typescript title="app/api/chat/route.ts" highlight={7-12} maxLines={50} import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; // Allow streaming responses up to 30 seconds export const maxDuration = 30; export async function POST(req: Request) { const { messages, id } = await req.json(); const result = streamText({ model: openai('gpt-4o'), messages, experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "path/to/directory", }, }, }); // Respond with the stream return result.toDataStreamResponse(); } ``` You can also group each step of a multi-step generation into a Flow by passing the `humanloopFlowPath` metadata value. ```typescript title="app/api/chat/route.ts" highlight={10-16} maxLines={50} import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; // Allow streaming responses up to 30 seconds export const maxDuration = 30; export async function POST(req: Request) { const { messages, id } = await req.json(); const result = streamText({ model: openai('gpt-4o'), messages, maxSteps: 3, toolCallStreaming: true, system: "You are a helpful assistant that answers questions about the weather in a given city.", experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "path/to/directory", } }, tools: { getWeatherInformation: { description: 'show the weather in a given city to the user', parameters: z.object({ city: z.string() }), execute: async ({}: { city: string }) => { const weatherOptions = ['sunny', 'cloudy', 'rainy', 'snowy', 'windy']; return { weather: weatherOptions[Math.floor(Math.random() * weatherOptions.length)], temperature: Math.floor(Math.random() * 50 - 10), }; } }, }, }); // Respond with the stream return result.toDataStreamResponse(); } ``` Node.js projects can use OpenTelemetry auto-instrumentation to trace requests without manually instrumenting code. Learn more about Node.js auto-instrumentation [here](https://opentelemetry.io/docs/languages/js/getting-started/nodejs). ### Set up OpenTelemetry Install dependencies. ```bash title="npm" wordWrap npm install dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` ```bash title="pnpm" wordWrap pnpm add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` ```bash title="yarn" wordWrap yarn add dotenv @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node ``` Add the following code to your file to initialize and clean up the OpenTelemetry SDK.Do not forget to call await sdk.shutdown() before your application shuts down in order to flush any remaining traces to Humanloop. ```typescript title="main.ts" import dotenv from 'dotenv'; import { NodeSDK } from "@opentelemetry/sdk-node"; import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node"; dotenv.config(); // Start the OpenTelemetry SDK const sdk = new NodeSDK({ instrumentations: [getNodeAutoInstrumentations()] }); sdk.start(); async function main() { // ... Your code here ... await sdk.shutdown(); } main().catch(console.error); ``` ### Configure OpenTelemetry Configure the [OpenTelemetry exporter](https://opentelemetry.io/docs/specs/otel/protocol/exporter/) to forward logs to Humanloop. ```plaintext title=".env.local" wordWrap HUMANLOOP_API_KEY=# Configure the OpenTelemetry OTLP Exporter OTEL_EXPORTER_OTLP_ENDPOINT=https://api.humanloop.com/v5/import/otel OTEL_EXPORTER_OTLP_PROTOCOL=http/json OTEL_EXPORTER_OTLP_HEADERS="X-API-KEY= " # Humanloop API key ``` ### Trace AI SDK calls Now add the `experimental_telemetry` parameter to your AI SDK function calls to trace them. With a simple one-step generation, each call to `streamText` or `generateText` will be traced as a Prompt Log on Humanloop. ```typescript title="main.ts" highlight={9-14} maxLines={50} import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; async function main() { // Example of a simple one-step generation const result = await streamText({ model: openai('gpt-4o'), messages, experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "path/to/directory", } } }); } ``` You can also group each step of a multi-step generation into a Flow by passing the `humanloopFlowPath` metadata value. ```typescript title="main.ts" highlight={12-18} maxLines={50} import { openai } from '@ai-sdk/openai'; import { streamText } from 'ai'; async function main() { // Example of a multi-step generation const result = await streamText({ model: openai('gpt-4o'), messages, maxSteps: 3, toolCallStreaming: true, system: "You are a helpful assistant that answers questions about the weather in a given city.", experimental_telemetry: { isEnabled: true, metadata: { "humanloop.directoryPath": "path/to/directory", } }, tools: { getWeatherInformation: { description: 'show the weather in a given city to the user', parameters: z.object({ city: z.string() }), execute: async ({}: { city: string }) => { const weatherOptions = ['sunny', 'cloudy', 'rainy', 'snowy', 'windy']; return { weather: weatherOptions[Math.floor(Math.random() * weatherOptions.length)], temperature: Math.floor(Math.random() * 50 - 10), }; } }, }, }); } ``` Our `.prompt` file format is a serialized representation of a [Prompt](/docs/explanation/prompts), designed to be human-readable and suitable for checking into your version control systems alongside your code. This allows technical teams to maintain the source of truth for their prompts within their existing version control workflow. ## Format The format is heavily inspired by [MDX](https://mdxjs.com/), with model and parameters specified in a YAML header alongside a JSX-inspired syntax for chat templates.
```jsx Chat --- model: gpt-4o temperature: 0.7 max_tokens: -1 provider: openai endpoint: chat --- ### Multi-modality and images Images can be specified using nested `You are a friendly assistant. ``` ```jsx Completion --- model: claude-2 temperature: 0.7 max_tokens: 256 top_p: 1.0 provider: anthropic endpoint: complete --- Autocomplete the sentence. Context: {{context}} {{sentence}} ```` tags within a ` ` message. To specify text alongside the image, use a ` ` tag. ```jsx Image and Text --- model: gpt-4o temperature: 0.7 max_tokens: -1 provider: openai endpoint: chat tools: [] --- You are a friendly assistant. ``` ### Tools, tool calls, and tool responses Specify the tools available to the model as a JSON list in the YAML header. Tool calls in assistant messages can be added with nested ` What is in this image? ` tags. A ` ` tag within an ` ` tag denotes a tool call of `type: "function"`, and requires the attributes `name` and `id`. The text wrapped in a ` ` tag should be a JSON-formatted string containing the tool call's arguments. Tool call responses can then be added with ` ` tags after the ` ` message. ```jsx --- model: gpt-4o temperature: 0.7 max_tokens: -1 provider: openai endpoint: chat tools: [ { "name": "get_current_weather", "description": "Get the current weather in a given location", "parameters": { "type": "object", "properties": { "location": { "type": "string", "name": "Location", "description": "The city and state, e.g. San Francisco, CA" }, "unit": { "type": "string", "name": "Unit", "enum": [ "celsius", "fahrenheit" ] } }, "required": [ "location" ] } } ] --- You are a friendly assistant. What is the weather in SF? { "location": "San Francisco, CA" } Cloudy with a chance of meatballs. ``` ``` ``` # Humanloop Runtime Environment > This reference provides details about the Python environment and supported packages. Humanloop allows you to specify the runtime for your code [Evaluators](../explanation/evaluators) and [Tool](../explanation/tools) implementations in order to run them natively with your Prompts in our Editor and UI based Evaluation workflows. ## Environment details Python version: **3.11.4** ``` anthropic==0.29.0 continuous-eval==0.3.13 jellyfish==1.1.0 jsonschema==4.22.0 langdetect==1.0.9 nltk==3.8.1 numpy==1.26.4 openai==1.35.10 pandas==2.2.2 pydantic==2.8.2 requests==2.32.3 scikit-learn==1.5.1 spacy==3.7.5 sqlglot==25.5.1 syllapy==0.7.2 textstat==0.7.3 transformers==4.43.4 ``` If you have any specific packages you would like to see here, please let us know at [support@humanloop.com](mailto:support@humanloop.com). # Security and Compliance > Learn about Humanloop's commitment to security, data protection, and compliance with industry standards. Humanloop is deeply committed to AI governance, security, and compliance. View our [Trust Report](https://trust.humanloop.com/) and [Policy Pages](https://humanloop.com/policies/privacy-policy) to see all of our certifications, request documentation, and view high-level details on the controls we adhere to. Humanloop never trains on user data. ## Humanloop Security Offerings: * **Data Privacy and Security** * Activate LLMs with your private data, safely and securely. You own your data and models. * **Monitoring & Support** * End-to-end monitoring of your AI applications, support guarantees from trusted AI experts. * Data Encryption * Data Management & AI Governance ## User Authentication and Access Control ### Authentication & Access Control - Humanloop Web App All users of the Humanloop web application require a valid email address and password to use the system: * Email addresses are verified on account creation. * Passwords are verified as sufficiently complex. * Passwords are stored using a one-way salted hash. * User access logs are maintained including date, time, user ID, relevant URL, operation performed, and source IP address for audit purposes. ### Authentication & Access Control - Humanloop API All users of the API are required to authenticate with a unique API token header: * Follows the OAuth 2.0 pattern. * API tokens are only visible once on creation and then obfuscated. * Users can manage the expiry of API keys. * API token access logs are maintained including date, time, user ID, relevant URL, operation performed, and source IP address for audit purposes. ### Additional Resources * Role-based access control (RBAC) - We implement strict role-based access control (RBAC) for all our systems. * Multi-factor authentication (MFA) - MFA is enforced for all employee accounts. ## Encryption Standards ### **Encryption** Humanloop follows best practices for data management and encryption. All data in transit is secured with TLS/SSL, and all data at rest is encrypted using the AES-256 algorithm. All encryption keys are managed using AWS Key Management Service (KMS) as part of the VPC definition. * All data in transit is encrypted using TLS 1.2 or higher. * Data at rest is encrypted using AES-256 encryption. ### **Infrastructure** All sensitive data is encrypted in transit. For Self-Hosted Cloud (VPC) environments, network traffic is also encrypted in transit and at rest to meet HIPAA requirements. Sensitive application data is only ever processed within the ECS cluster and stored in Aurora. To request a network infrastructure diagram or more information, please contact [privacy@humanloop.com](mailto:privacy@humanloop.com). **Learn More** For more information about how Humanloop processes user data, visit our Data Management & Hosting Options page. ## Security Certifications ### SOC2 Type II Compliance Humanloop is fully SOC2 Type II compliant. Learn more via our [Trust Center](https://trust.humanloop.com/) and our [Security Policy](https://humanloop.com/policies/security-policy) page. ### HIPAA Compliance Humanloop actively works with paying customers to help them achieve HIPAA compliance. Official certification is pending. To request references or more information, contact [sales@humanloop.com](mailto:sales@humanloop.com). **HIPAA Compliance via Hosting Environment:** Humanloop offers dedicated platform instances on AWS with HIPAA provisions for enterprise customers that have particularly sensitive data. These provisions include: * The ability for enterprises to manage their own encryption keys. * A specific AWS Fargate deployment that follows HIPAA practices. ### GDPR Compliance We are fully compliant with the General Data Protection Regulation (GDPR). This includes: * Data minimization practices * User rights management * Data processing agreements ## **How Humanloop helps customers maintain compliance:** * Self-Hosted Cloud (VPC) environments * Data Processing Agreements (DPAs) * Data Minimization and Retention Policies * Role-Based Access Controls * Data Encryption * Robust Security Measures * Incident Response Plan SLAs * Regular Training & Audits ### Learn more: * Cloud Hosting Options * Data Management Protocols * [Security Policy](https://humanloop.com/policies/security-policy) * [Privacy Policy](https://humanloop.com/policies/privacy-policy) * [Trust Center](https://trust.humanloop.com/) To request references or more information, contact [sales@humanloop.com](mailto:sales@humanloop.com) # Data Management > Discover Humanloop's robust data management practices and state-of-the-art encryption methods ensuring maximum security and compliance for AI applications. ### Data Handling and Segregation Separate environments are provisioned and maintained for development, quality assurance/user acceptance testing, and production to ensure data segregation at the environment level. ### Data Classification & Access Control All platform data received from the user and data derived from user data is classified as sensitive. All platform audit and telemetry data that does not contain PII and reference to specific user data is classified as not sensitive. By default, only authenticated users can see their own sensitive data. Data classified as not sensitive can be accessed by dedicated Humanloop support staff using a secure VPN connection to the private network of the VPC for the target environment. This access is for debugging issues and improving system performance. The Terms of Service define further details around data ownership and access on a case-by-case basis. ### Data Encryption and Security #### Encryption Humanloop follows best practices for data management and encryption. All data in transit is secured with TLS/SSL, and all data at rest is encrypted using the AES-256 algorithm. All encryption keys are managed using AWS Key Management Service (KMS) as part of the VPC definition. ### Infrastructure All sensitive data is encrypted in transit. For Self-Hosted Cloud (VPC) environments, network traffic is also encrypted in transit and at rest to meet HIPAA requirements. Sensitive application data is only processed within the ECS cluster and stored in Aurora. To request a network infrastructure diagram or more information, please contact [privacy@humanloop.com](mailto:privacy@humanloop.com). ### Learn More For more information on how Humanloop processes user data, visit our [Security & Compliance](https://trust.humanloop.com) page. ### Data Storage, Retention, and Recovery All platform data is stored in a primary database server with multi-availability zone replication. Platform data is retained indefinitely and backed up daily in a secure and encrypted manner until a request is made by the contractual owners of that data to remove it, in accordance with GDPR guidelines. Humanloop's Terms of Service define the contractual owner of the user data and data derived from the user data. A semi-automated disaster recovery process is in place to restore the database to a specified point-in-time backup as required. ### Data Breach Response Any data breaches will be communicated to all impacted Humanloop users and partners within 24 hours, along with consequences and mitigations. Breaches will be dealt with in accordance with the Humanloop data breach response policy, which is tested annually. ### Data Portability and Return Within 30 days post-contract termination, users can request the return of their data and derived data (as defined by the Terms of Service). Humanloop provides this data via downloadable files in comma-separated value (.csv) or .json formats. # Access roles (RBACs) > Learn about the different roles and permissions in Humanloop to help you with prompt and data management for large language models. Everyone invited to the organization can access all projects currently (controlling project access coming soon). A user can be one of the following rolws: **Admin:** The highest level of control. They can manage, modify, and oversee the Organization's settings and have full functionality across all projects. **Developer:** (Enterprise tier only) Can deploy Files, manage environments, create and add API keys, but lacks the ability to access billing or invite others. **Member:** (Enterprise tier only) The basic level of access. Can create and save Files, run Evaluations, but not deploy. Can not see any org-wide API keys. ## RBACs summary Here is the full breakdown of roles and access: | Action | Member | Developer | Admin | | :----------------------------- | :----- | :-------- | :---- | | Create and manage Files | ✔️ | ✔️ | ✔️ | | Inspect logs and feedback | ✔️ | ✔️ | ✔️ | | Create and manage Evaluators | ✔️ | ✔️ | ✔️ | | Run Evaluations | ✔️ | ✔️ | ✔️ | | Create and manage Datasets | ✔️ | ✔️ | ✔️ | | Create and manage API keys | | ✔️ | ✔️ | | Manage prompt deployments | | ✔️ | ✔️ | | Create and manage environments | | ✔️ | ✔️ | | Send invites | | | ✔️ | | Set user roles | | | ✔️ | | Manage billing | | | ✔️ | | Change Organization settings | | | ✔️ | # SSO and Authentication > Learn about Single Sign-On (SSO) and authentication options for Humanloop {/* WIP - for gartner /start */} Humanloop offers authentication options to ensure secure access to your organization's resources. This guide covers our Single Sign-On (SSO) capabilities and other authentication methods. ## Single Sign-On (SSO) Single Sign-On allows users to access multiple applications with a single set of credentials. Humanloop supports SSO integration with major identity providers, enhancing security and simplifying user management. ### Supported SSO Providers * Google Workspace * Okta * Azure Active Directory * OneLogin * Custom SAML 2.0 providers ### Benefits of SSO 1. Enhanced security with centralized authentication 2. Simplified user management 3. Improved user experience with reduced password fatigue 4. Streamlined onboarding and offboarding processes ### Setting up SSO To set up SSO for your organization: 1. Contact our sales team to enable SSO for your account 2. Choose your identity provider 3. Configure the connection between Humanloop and your identity provider 4. Test the SSO integration 5. Roll out to your users ## Multi-Factor Authentication (MFA) For accounts not using SSO, we strongly recommend enabling Multi-Factor Authentication for an additional layer of security. ### MFA Options * Time-based One-Time Password (TOTP) apps * SMS-based verification * Hardware security keys (e.g., YubiKey) ## API Authentication For programmatic access to Humanloop, we use API keys. These should be kept secure and rotated regularly. ### Managing API Keys * Generate API keys in your account settings * Use environment variables to store API keys in your applications * Implement key rotation policies for enhanced security ## User Provisioning and Deprovisioning Humanloop supports automated user lifecycle management through our Directory Sync feature. This allows for: * Automatic user creation based on directory group membership * Real-time updates to user attributes and permissions * Immediate deprovisioning when users are removed from directory groups ## Best Practices 1. Use SSO when possible for centralized access control 2. Enable MFA for all user accounts 3. Regularly audit user access and permissions 4. Implement the principle of least privilege 5. Use secure protocols (HTTPS) for all communications with Humanloop For more information on setting up SSO or other authentication methods, please contact our support team or refer to our API documentation. ## Active Directory Sync Humanloop supports Active Directory Sync for automated user provisioning and deprovisioning. This feature allows you to: * Automatically create and update user accounts based on your Active Directory groups * Sync user attributes and roles in real-time * Instantly deprovision access when users are removed from AD groups * Maintain consistent access control across your organization * Reduce manual user management tasks and potential security risks To set up Active Directory Sync: 1. Contact our sales team to enable this feature for your account 2. Configure the connection between Humanloop and your Active Directory 3. Map your AD groups to Humanloop roles and permissions 4. Test the sync process with a small group of users 5. Roll out to your entire organization For more information on implementing Active Directory Sync, please contact our [support team](mailto:support@humanloop.com). {/* WIP - for gartner /end */} # LLMs.txt > Humanloop docs are accessible to AI tools using the llms.txt standard. ## What is llms.txt? LLMs.text is an [emerging standard](https://llmstxt.org/) so that websites can easily expose information to AI. We have implemented it for the Humanloop docs. ### An overview of the Humanloop docs The llms.txt file contains an overview of the Humanloop docs with links to each page./llms.txt for an overview of the Humanloop docs. ### The full content of the Humanloop docs The llms-full.txt file contains the full content of the Humanloop docs and API reference.
* **Small and fast**: Quick to load and easy to parse * **Summary-focused**: Each page with one-sentence description with its URL * **Structured for AI**: Helps tools understand the structure of the docs/llms-full.txt for the full content of the Humanloop docs and API reference Note that this might take up to 10 seconds to load. ### Raw markdown on any page available And on any specific page, you can add **`.md`** to the end of the URL to get the raw markdown content of the page.
* **Comprehensive**: Includes the full content of your documentation * **API knowledge**: Incorporates the full API reference and SDK snippets * **Convenient**: One giant payload for all the docsExample of the raw markdown for the Call Prompt page ## How to use llms.txt You can copy the URL and paste that into Cursor, ChatGPT or any other AI tool that can load a URL to give it as context to your LLM. * [https://humanloop.com/docs/llms.txt](https://humanloop.com/docs/llms.txt) * [https://humanloop.com/docs/llms-full.txt](https://humanloop.com/docs/llms-full.txt) If the AI tool you are using doesn't support loading a URL, you can copy the content and paste it into the prompt. # Overview > Learn how to integrate Humanloop into your applications using our Python and TypeScript SDKs or REST API. The Humanloop platform can be accessed through the [API](/docs/v5/api) or through our Python and TypeScript SDKs.
Add **`.md`** to the end of the URL to get the raw markdown content of the page.### Usage Examples # Errors > This page provides a list of the error codes and messages you may encounter when using the Humanloop API. ### HTTP error codes Our API will return one of the following HTTP error codes in the event of an issue: ```shell title="Installation" npm install humanloop ``` ```typescript title="Example usage" import { HumanloopClient } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` ```shell title="Installation" pip install humanloop ``` ```python title="Example usage" from humanloop import Humanloop hl = Humanloop(api_key=" ") # Check that the authentication was successful print(hl.prompts.list()) ``` ## Error details Our `prompt/call` endpoint acts as a unified interface across all popular model providers. The error returned by this endpoint may be raised by the model provider's system. Details of the error are returned in the `detail` object of the response. ```json { "type": "unprocessable_entity_error", "message": "This model's maximum context length is 4097 tokens. However, you requested 10000012 tokens (12 in the messages, 10000000 in the completion). Please reduce the length of the messages or completion.", "code": 422, "origin": "OpenAI" } ``` # Decorators Overview > Overview of the decorator system in the Humanloop SDK ## Introduction Humanloop provides a set of decorators that help you instrument your AI features with minimal code changes. These decorators automatically create and manage Logs on the Humanloop platform, enabling monitoring, evaluation, and improvement of your AI applications. | Decorator | Purpose | Creates | Documentation | |-----------|---------|---------|---------------| | `prompt` | Instrument LLM provider calls | Prompt Logs | [Learn more →](/docs/v5/sdk/decorators/prompt) | | `tool` | Define function calling tools | Tool Logs | [Learn more →](/docs/v5/sdk/decorators/tool) | | `flow` | Trace multi-step AI features | Flow Log with traces | [Learn more →](/docs/v5/sdk/decorators/flow) | ## Common Patterns All decorators share these common characteristics: - **Path-based organization**: Each decorator requires a `path` parameter that determines where the File and its Logs are stored in your Humanloop workspace. - **Automatic versioning**: Changes to the decorated function or its parameters create new versions of the File. - **Error handling**: Errors are caught and logged, making debugging easier. - **Minimal code changes**: Decorate existing code and adopt the Humanloop SDK gradually. # Flow Decorator > Technical reference for the Flow decorator in the Humanloop SDK ## Overview The Flow decorator creates and manages traces for your AI feature. When applied to a function, it: - Creates a new trace on function invocation. - Adds all Humanloop logging calls made inside the function to the trace. - Completes the trace when the function exits. Your request was improperly formatted or presented. Your API key is incorrect or missing, or your user does not have the rights to access the relevant resource. The requested resource could not be located. Modifying the resource would leave it in an illegal state. Your request was properly formatted but contained invalid instructions or did not match the fields required by the endpoint. You've exceeded the maximum allowed number of requests in a given time period. An unexpected issue occurred on the server. The service is temporarily overloaded and you should try again. On Humanloop, a trace is the collection of Logs associated with a Flow Log. ## Usage The `flow` decorator will trace all downstream Humanloop logs, whether they are created by other decorators or SDK calls. ### Tracing Decorators```python maxLines=50 wrapLines title="Python" @hl_client.prompt(path="MyFeature/Call LLM"): def call_llm(messages: List[ChatMessage]): return openai.chat.completions.create( model="gpt-4o-mini", messages=messages ).choices[0].message.content @hl_client.flow(path="MyFeature/Process") def process_input(inputs: list[str]) -> list[str]: # Logs created by the Prompt decorator are added to the trace return [ call_llm([{"role": "user", "content": text}]) for text in inputs ] ``` ```typescript maxLines=50 wrapLines title="TypeScript" const callLLM = hlClient.prompt({ path: "MyFeature/Call LLM", callable: async (messages: ChatMessage[]): Promise ### Tracing SDK Calls Logs created through the Humanloop SDK are added to the trace.=> { const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages }); return response.choices[0].message.content; } }); const processInput = hlClient.flow({ path: "MyFeature/Process", callable: async (inputs: string[]): Promise => { // Logs created by the Prompt decorator are added to the trace return inputs.map(async (text) => await callLLM([ {"role": "user", "content": text} ])); }); ``` ```python maxLines=50 title="Python" wrapLines @hl_client.flow(path="MyFeature/Process") def process_input(text: str) -> str: # Created Log is added to the trace llm_output = hl_client.prompts.call( path="MyFeature/Transform", messages=[{"role": "user", "content": text}] ).logs[0].output_message.content transformed_output = transform(llm_output) # Created Log is added to the trace hl_client.tools.log( path="MyFeature/Transform", tool={function: TRANSFORM_JSON_SCHEMA}, inputs={"text": text}, output=transformed_output ) return transformed_output ``` ```typescript maxLines=50 const processInput = hlClient.flow({ path: "MyFeature/Process", callable: async (text: string): Promise ## Behavior=> { // Created Log is added to the trace const llmOutput = ( await hlClient.prompts.call({ path: "MyFeature/Transform", messages: [{ role: "user", content: text }], }) ).logs[0].outputMessage.content; const transformedOutput = transform(llmOutput); // Created Log is added to the trace await hlClient.tools.log({ path: "MyFeature/Transform", tool: { function: TRANSFORM_JSON_SCHEMA }, inputs: { text }, output: transformedOutput, }); return transformedOutput; }, }); ``` ## Definition The decorated function creates a Flow Log when called. All Logs created inside the decorated function are added to its trace. The Flow Log's fields are populated as follows: | Field | Type | Description | | ---------------- | ----------- | -------------------------------------------------------------------- | | `inputs` | object | Function arguments that aren't ChatMessage arrays | | `messages` | array | ChatMessage arrays passed as arguments | | `output_message` | ChatMessage | Return value if it's a ChatMessage-like object | | `output` | string | Stringified return value if not a ChatMessage-like object | | `error` | string | Error message if function throws or return value can't be serialized | If the decorated function returns a ChatMessage object, the `output_message` field is populated. Otherwise, the `output` field is populated with the stringified return value. The decorated function creates a Flow Log when called. All Logs created inside the decorated function are added to its trace. The Flow Log's fields are populated as follows: | Field | Type | Description | | --------------- | ----------- | -------------------------------------------------------------------- | | `inputs` | object | Function arguments that aren't ChatMessage arrays | | `messages` | array | ChatMessage arrays passed as arguments | | `outputMessage` | ChatMessage | Return value if it's a ChatMessage-like object | | `output` | string | Stringified return value if not a ChatMessage-like object | | `error` | string | Error message if function throws or return value can't be serialized | If the decorated function returns a ChatMessage object, the `outputMessage` field is populated. Otherwise, the `output` field is populated with the stringified return value. The decorator accepts the following parameters: | Parameter | Type | Required | Description | | ------------ | ------ | -------- | ---------------------------------------- | | `path` | string | Yes | Path on Humanloop workspace for the Flow | | `attributes` | object | No | Key-value object for versioning the Flow | ## SDK Interactions ```python @hl_client.flow( # Required: path on Humanloop workspace for the Flow path: str, # Optional: metadata for versioning the Flow attributes: dict[str, Any] = None ) def function(*args, **kwargs): ... ``` The decorator will preserve the function's signature. ```typescript hlClient.flow({ // Required: path on Humanloop workspace for the Flow path: string, // Required: decorated function callable: I extends Record & { messages: ChatMessage[] } ? (inputs: I) => O : () => O; // Optional: metadata for versioning the Flow attributes?: Record ; }) => Promise ``` The function returned by the decorator is async and preserves the signature of `callable`. Callable's `inputs` must extend `Record `. If a `messages` field is present in the `inputs`, it must have the `ChatMessage[]` type. The decorated function will not wrap the return value in a second Promise if the `callable` is also asynchronous. ## Error Handling - It's not possible to call `flows.log()` inside a decorated function. This will raise a [`HumanloopRuntimeError`](#error-handling) - To create nested traces, call another flow-decorated function. - Passing `trace_parent_id` argument to an SDK logging call inside the decorated function is ignored and emits a warning; the Log is added to the trace of the decorated function. - It's not possible to call `flows.log()` inside a decorated function. This will raise a [`HumanloopRuntimeError`](#error-handling) - To create nested traces, call another flow-decorated function. - Passing `traceParentId` argument to an SDK logging call inside the decorated function is ignored and emits a warning; the Log is added to the trace of the decorated function. ## Related Documentation A explanation of Flows and their role in the Humanloop platform is found in our [Flows](/docs/v5/explanation/flows) documentation. # Prompt Decorator > Technical reference for the Prompt decorator in the Humanloop SDK ## Overview The Prompt decorator automatically instruments LLM provider calls and creates Prompt Logs on Humanloop. When applied to a function, it: - Creates a new Log for each LLM provider call made within the decorated function. - Versions the Prompt using hyperparameters of the provider call. ### Decorator Definition - If user-written code (e.g. in code Evaluators) raises an exception, the relevant Log's `error` field is populated with the exception message and the decorated function returns `None`. - `HumanloopRuntimeError` exceptions indicate incorrect decorator or SDK usage and are re-raised instead of being logged under `error`. - If user-written code (e.g. in code Evaluators) throws an exception, the relevant Log's `error` field is populated with the exception message and the decorated function returns `undefined`. - `HumanloopRuntimeError` exceptions indicate incorrect decorator or SDK usage and are re-thrown instead of being logged under `error`. ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `path` | string | Yes | Path on Humanloop workspace for the Prompt | ### Usage ```python @hl_client.prompt( # Required: path on Humanloop workspace for the Prompt path: str ) def function(*args, **kwargs): ... ``` The decorated function will have the same signature as the original function. ```typescript hlClient.prompt({ // Required: path on Humanloop workspace for the Prompt path: string, // Required: decorated function callable: I extends Record & { messages?: ChatMessage[] } ? (args: I) => O : () => O; }) => Promise ``` The decorated function is always async and has the same signature as the `callable` argument. Callable's `args` must extend `Record `. If a `messages` field is present in the `args`, it must have type `ChatMessage[]`. The decorated function will not wrap the return value in a second Promise if the `callable` is also asynchronous. You must pass the providers you want to auto-instrument to the HumanloopClient constructor. Otherwise, the decorated function will work, but no Logs will be created. ```typescript {6-7} import { HumanloopClient } from "humanloop"; import { OpenAI } from "openai"; const hlClient = new HumanloopClient({ apiKey: process.env.HL_API_KEY, // Pass the provider module here providers: { OpenAI } }) // You can now use the prompt decorator ``` ```python @hl_client.prompt(path="MyFeature/Process") def process_input(text: str) -> str: return openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": text}] ).choices[0].message.content ``` ```typescript const processInput = hlClient.prompt({ path: "MyFeature/Process", callable: async (text: string): Promise ## Behavior ### Versioning The hyperparameters of the LLM provider call are used to version the Prompt. If the configuration changes, new Logs will be created under the new version of the the same Prompt. The following parameters are considered for versioning the Prompt: | Parameter | Description | |-----------|-------------| | `model` | The LLM model identifier | | `endpoint` | The API endpoint type | | `provider` | The LLM provider (e.g., "openai", "anthropic") | | `max_tokens` | Maximum tokens in completion | | `temperature` | Sampling temperature | | `top_p` | Nucleus sampling parameter | | `presence_penalty` | Presence penalty for token selection | | `frequency_penalty` | Frequency penalty for token selection | ### Log Creation Each LLM provider call within the decorated function creates a Log with the following fields set:=> { return openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: text }] }).choices[0].message.content; } }); ``` ## Error Handling | Field | Type | Description | |-------|------|-------------| | `inputs` | dict[str, Any] | Function arguments that aren't ChatMessage arrays | | `messages` | ChatMessage[] | ChatMessage arrays passed to the LLM | | `output_message` | ChatMessage | LLM response with role and content | | `error` | string | Error message if the LLM call fails | | `prompt_tokens` | int | Number of tokens in the prompt | | `reasoning_tokens` | int | Number of tokens used in reasoning | | `output_tokens` | int | Number of tokens in the completion | | `finish_reason` | string | Reason the LLM stopped generating | | `start_time` | datetime | When the LLM call started | | `end_time` | datetime | When the LLM call completed | | Field | Type | Description | |-------|------|-------------| | `inputs` | object | Function arguments that aren't ChatMessage arrays | | `messages` | ChatMessage[] | ChatMessage arrays passed to the LLM | | `output_message` | ChatMessage | LLM response with role and content | | `error` | string | Error message if the LLM call fails | | `prompt_tokens` | number | Number of tokens in the prompt | | `reasoning_tokens` | number | Number of tokens used in reasoning | | `output_tokens` | number | Number of tokens in the completion | | `finish_reason` | string | Reason the LLM stopped generating | | `start_time` | Date | When the LLM call started | | `end_time` | Date | When the LLM call completed | ## Best Practices 1. Multiple Logs will be created if you make multiple calls inside the decorated function. To avoid confusion, avoid calls with different providers or hyperparameters, as this will create multiple versions of the Prompt. 2. Calling `prompts.log()` or `prompts.call()` inside the decorated function works normally, with no interaction with the decorator. However, it indicates a misuse of the decorator, as they are alternatives for achieving the same result. 3. If you want to switch between providers with ease, use [`prompts.call()`](/docs/v5/reference/prompts/call) with a `provider` parameter instead of the decorator. ## Related Documentation Humanloop Prompts are more than the string passed to the LLM provider. They encapsulate LLM hyperparameters, associations to available tools, and can be templated. For more details, refer to our [Prompts explanation](/docs/v5/explanation/prompts). # Tool Decorator > Technical reference for the Tool decorator in the Humanloop SDK ## Overview The Tool decorator helps you define [Tools](/docs/v5/explanation/tools) for use in function calling. It automatically instruments function calls and creates Tool Logs on Humanloop. - LLM provider errors are caught and logged in the Log's `error` field. However, `HumanloopRuntimeError` is not caught and will be re-raised: they indicate wrong SDK or decorator usage. - The decorated function propagates exceptions from the LLM provider. - LLM provider errors are caught and logged in the Log's `error` field. However, `HumanloopRuntimeError` is not caught and will be re-thrown: they indicate wrong SDK or decorator usage. - The decorated function propagates exceptions from the LLM provider. ### Definition Calling a decorated function will create a Tool Log with the following fields: - `inputs`: The function arguments. - `output`: The function return value. - `error`: The error message if the function call fails. Calling a decorated function will create a Tool Log with the following fields: - `inputs`: The function arguments. - `output`: The function return value. - `error`: The error message if the function call fails. ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `path` | string | Yes | Path on Humanloop workspace for the Tool | | `attributes` | object | No | Additional metadata for the Tool (Python only) | | `setup_values` | object | No | Values needed to setup the Tool (Python only) | | `version` | ToolKernelRequest | Yes | JSON Schema for the Tool (TypeScript only) | ### Usage ```python @hl_client.tool( # Required: path on Humanloop workspace for the Tool path: str, # Optional: additional metadata for the Tool attributes: Optional[dict[str, Any]] = None, # Optional: values needed to setup the Tool setup_values: Optional[dict[str, Any]] = None ) def function(*args, **kwargs): ... ``` The decorated function will have the same signature as the original function and will have a `json_schema` attribute containing the inferred JSON Schema. ```typescript hlClient.tool({ // Required: path on Humanloop workspace for the Tool path: string, // Required: decorated function callable: I extends Record ? (args: I) => O : () => O, // Required: JSON Schema for the Tool version: ToolKernelRequest }) => Promise ``` The decorated function is always async and has the same signature as the `callable` argument. It will have a `jsonSchema` attribute containing the provided JSON Schema. ## Behavior ### Schema Definition ```python @hl_client.tool(path="MyFeature/Calculator") def calculator(a: int, b: Optional[int] = None) -> int: """Add two numbers together.""" return a + (b or 0) ``` Decorating a function will set a `json_schema` attribute that can be used for function calling. ```python {5, 12-14} # Use with prompts.call response = hl_client.prompts.call( path="MyFeature/Assistant", messages=[{"role": "user", "content": "What is 5 + 3?"}], tools=[calculator.json_schema] ) # Or with OpenAI directly! response = openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What is 5 + 3?"}], tools=[{ "type": "function", "function": calculator.json_schema }] ) ``` ```typescript maxLines=50 const calculator = hlClient.tool({ path: "MyFeature/Calculator", callable: (inputs: { a: number; b?: number }) => { return inputs.a + (inputs.b || 0); }, version: { function: { name: "calculator", description: "Add two numbers together.", parameters: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a"] } } } }); ``` Decorating a function will set a `jsonSchema` attribute that can be used for function calling. ```typescript {5, 12-14} // Use with prompts.call const response = await hlClient.prompts.call({ path: "MyFeature/Assistant", messages: [{ role: "user", content: "What is 5 + 3?" }], tools: [calculator.jsonSchema] }); // Or with OpenAI directly! const response = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "What is 5 + 3?" }], tools: [{ type: "function", function: calculator.jsonSchema }] }); ``` ### Log Creation Each function call creates a Tool Log with the following fields: In Python, the decorator automatically infers a JSON Schema from the source code, argument signature, and docstrings: - Function name becomes the tool name - Function docstring becomes the tool description - Parameter type hints are converted to JSON Schema types - Optional parameters (using `Optional[T]` or `T | None`) are marked as not required - Return type is not included in the schema Supported type hints: | Python Type | JSON Schema Type | |-------------|------------------| | `str` | `"string"` | | `int` | `"integer"` | | `float` | `"number"` | | `bool` | `"boolean"` | | `list[T]` | `"array"` with items of type T | | `dict[K, V]` | `"object"` with properties of types K and V | | `tuple[T1, T2, ...]` | `"array"` with items of specific types | | `Optional[T]` or `T \| None` | Type T with `"null"` added | | `Union[T1, T2, ...]` | `"anyOf"` with types T1, T2, etc. | | No type hint | `any` | In TypeScript, you must provide a JSON Schema in the `version` parameter: ```typescript version: { function: { name: string; description: string; parameters: { type: "object"; properties: Record ; required?: string[]; }; }; attributes?: Record ; setup_values?: Record ; } ``` ## Error Handling | Field | Type | Description | |-------|------|-------------| | `inputs` | dict[str, Any] | Function arguments | | `output` | string | JSON-serialized return value | | `error` | string | Error message if the function call fails | | Field | Type | Description | |-------|------|-------------| | `inputs` | object | Function arguments | | `output` | string | JSON-serialized return value | | `error` | string | Error message if the function call fails | ## Best Practices - Function errors are caught and logged in the Log's `error` field. - The decorated function returns `None` when an error occurs. - `HumanloopRuntimeError` is not caught and will be re-raised, as it indicates incorrect SDK or decorator usage. - Function errors are caught and logged in the Log's `error` field. - The decorated function returns `undefined` when an error occurs. - Schema validation errors are thrown if the inputs don't match the schema. - `HumanloopRuntimeError` is not caught and will be re-thrown, as it indicates incorrect SDK or decorator usage. ## Related Documentation For a deeper understanding of Tools and their role in the Humanloop platform, refer to our [Tools](/docs/v5/explanation/tools) documentation. For attaching a Tool to a Prompt, see [Tool calling in Editor](/docs/v5/guides/prompts/tool-calling-editor) and [linking a Tool to a Prompt](/docs/v5/guides/prompts/link-tool). # Run Evaluation > Getting up and running with Humanloop is quick and easy. This guide will explain how to set up evaluations on Humanloop and use them to iteratively improve your applications. The `evaluations.run()` function is a convenience function that allows you to trigger evaluations from code. It will create the evaluation, fetch the dataset, generate all the Logs and then run the evaluators on each log. It supports evaluating arbitrary functions, Prompts stored on Humanloop, and Prompts defined in code. ## Parameters You can see the source code for the `evaluations.run()` function in [Python](https://github.com/humanloop/humanloop-python/blob/master/src/humanloop/evals/run.py#L106) and [TypeScript](https://github.com/humanloop/humanloop-node/blob/master/src/evals/run.ts#L211). 1. Use clear and descriptive docstrings in Python to provide good tool descriptions 2. Ensure all function parameters have appropriate type hints in Python 3. Make return values JSON-serializable 4. Use the `json_schema` attribute when passing the tool to `prompts.call()` 1. Use clear and descriptive docstrings in TypeScript to provide good tool descriptions 2. Ensure all function parameters have appropriate type hints in TypeScript 3. Make return values JSON-serializable 4. Use the `jsonSchema` attribute when passing the tool to `prompts.call()` Name of the evaluation to help identify it Configuration for what is being evaluated. The evaluation will be stored on this File. Path to the evaluated File (a [Prompt](/docs/explanation/prompts), [Flow](/docs/explanation/flows), [Tool](/docs/explanation/tools), [Evaluator](/docs/explanation/evaluators) etc.) on Humanloop. If the File does not exist on Humanloop, it will be created. Example: `My Agent` will create a `flow` file on Humanloop. `flow` (default), `prompt`, `tool`, `evaluator` If the File does not exist on Humanloop, it will be created with this File type. Pass in the details of the version of the File you want to evaluate. For example, for a Flow you might pass in identifiers: ```json { "git_hash": "1234567890", "identifier": "rag-with-pinecone" } ``` Or for a Prompt you can pass in Prompt details and it will be called. ```json { "model": "gpt-4", "template": [ { "role": "user", "content": "You are a helpful assistant on the topic of {{topic}}." } ] } ``` Function to evaluate (optional if the File is runnable on Humanloop like a Prompt). It will be called using your Dataset `callable(**datapoint.inputs, messages=datapoint.messages)`. It should return a single string output. List of evaluators to judge the generated output Path to evaluator on Humanloop The type of arguments the Evaluator expects - only required for local Evaluators The type of return value the Evaluator produces - only required for local Evaluators Function to evaluate (optional if the Evaluator is runnable on Humanloop). It will be called using the generated output as follows: `callable(output)`. It should return a single string output. Optional function that logs the output judgment from your Evaluator to Humanloop. If provided, it will be called as: `judgment = callable(log_dict); log = custom_logger(client, judgment)`. Inside the custom_logger, you can use the Humanloop `client` to log the judgment to Humanloop. If not provided your function must return a single string and by default the code will be used to inform the version of the external Evaluator on Humanloop. The threshold to check the evaluator result against Dataset to evaluate against ## Return Type Returns an `EvaluationStats` object containing: - run_stats: Array of statistics for each run - progress: Summary of evaluation progress - report: Detailed evaluation report - status: Current status of evaluation # Examples ## 1. Evaluating an Arbitrary Flow Function To evaluate an arbitrary workflow you can pass in the `callable` parameter to the `file` object.Path to existing dataset on Humanloop. If the Dataset does not exist on Humanloop, it will be created. The datapoints to map your function over to produce the outputs required by the evaluation. Optional - if not provided, the evaluation will be run over the datapoints stored on Humanloop. ```python def my_flow_function(messages): # Your custom logic here return "Response based on messages" evaluation = humanloop.evaluations.run( name="Custom Flow Evaluation", type="flow", file={ "path": "Custom/Flow", "callable": my_flow_function }, evaluators=[ {"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Latency"} ], dataset={ "path": "Test/Dataset", "datapoints": [ { "messages": [ {"role": "user", "content": "Test question 1"} ] } ] } ) ``` ```typescript const myFlowFunction = (messages: Message[]): string => { // Your custom logic here return "Response based on messages"; }; const evaluation = await humanloop.evaluations.run({ name: "Custom Flow Evaluation", file: { path: "Custom/Flow", type: "flow", callable: myFlowFunction, }, evaluators: [ { path: "Example Evaluators/AI/Semantic similarity" }, { path: "Example Evaluators/Code/Latency" }, ], dataset: { path: "Test/Dataset", datapoints: [ { messages: [{ role: "user", content: "Test question 1" }], }, ], }, }); ``` ## 2. Evaluating a Prompt on Humanloop To evaluate a Prompt stored on Humanloop you simply supply a `path` to the Prompt and a list of Evaluators.```python evaluation = humanloop.evaluations.run( name="Existing Prompt Evaluation", file={ "path": "Existing/Prompt", }, evaluators=[ {"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Cost"} ], dataset={ "path": "Existing/Dataset" } ) ``` ```typescript const evaluation = await humanloop.evaluations.run({ name: "Existing Prompt Evaluation", file: { path: "Existing/Prompt", }, evaluators: [ { path: "Example Evaluators/AI/Semantic similarity" }, { path: "Example Evaluators/Code/Cost" }, ], dataset: { path: "Existing/Dataset", }, }); ``` ## 3. Evaluating a Prompt in Code To evaluate a Prompt defined in code you can pass in the `model`, `template` and other Prompt parameters to the `file`'s `version` object.```python evaluation = humanloop.evaluations.run( name="Code Prompt Evaluation", file={ "path": "Code/Prompt", "version": { "model": "gpt-4", "template": [ { "role": "system", "content": "You are a helpful assistant on the topic of {{topic}}." } ] }, }, evaluators=[ {"path": "Example Evaluators/AI/Semantic similarity"}, {"path": "Example Evaluators/Code/Latency"} ], dataset={ "datapoints": [ { "inputs": { "topic": "machine learning" }, "messages": [ {"role": "user", "content": "What is machine learning?"} ], "target": { "output": "Machine learning is a subset of artificial intelligence..." } } ] } ) ``` ```typescript const evaluation = await humanloop.evaluations.run({ name: "Code Prompt Evaluation", file: { path: "Code/Prompt", model: "gpt-4", template: [ { role: "system", content: "You are a helpful assistant on the topic of {{topic}}.", }, ], }, evaluators: [ { path: "Example Evaluators/AI/Semantic similarity" }, { path: "Example Evaluators/Code/Latency" }, ], dataset: { datapoints: [ { inputs: { topic: "machine learning" }, messages: [{ role: "user", content: "What is machine learning?" }], target: { output: "Machine learning is a subset of artificial intelligence...", }, }, ], }, }); ``` Each example demonstrates a different way to use the `evaluation.run` function. The function returns evaluation statistics that can be used to understand the performance of your LLM application according to the specified evaluators. You can view the results of your evaluation in the Humanloop UI by navigating to the specified file path, or by checking the evaluation stats programmatically using the returned object's `report` field. # API The Humanloop API allows you to interact with Humanloop and model providers programmatically. You can do this through HTTP requests from any language or via our official Python or TypeScript SDK.First you need to install and initialize the SDK. If you have already done this, skip to the next section. Open up your terminal and follow these steps: 1. Install the Humanloop SDK: Guides and further details about key concepts can be found in [our docs](/docs/getting-started/overview). # Log to a Prompt ```http POST https://api.humanloop.com/v5/prompts/log Content-Type: application/json ``` Log to a Prompt. You can use query parameters `version_id`, or `environment`, to target an existing version of the Prompt. Otherwise, the default deployed version will be chosen. Instead of targeting an existing version explicitly, you can instead pass in Prompt details in the request body. In this case, we will check if the details correspond to an existing version of the Prompt. If they do not, we will create a new version. This is helpful in the case where you are storing or deriving your Prompt details in code. ## Query Parameters - VersionId (optional): A specific Version ID of the Prompt to log to. - Environment (optional): Name of the Environment identifying a deployed version to log to. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell Log prompt curl -X POST https://api.humanloop.com/v5/prompts/log \ -H "X-API-KEY:```python pip install humanloop ``` ```typescript npm install humanloop ``` 2. Initialize the SDK with your Humanloop API key (you can get it from the [Organization Settings page](https://app.humanloop.com/account/api-keys)).```python from humanloop import Humanloop humanloop = Humanloop(api_key=" ") # Check that the authentication was successful print(humanloop.prompts.list()) ``` ```typescript import { HumanloopClient, Humanloop } from "humanloop"; const humanloop = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); // Check that the authentication was successful console.log(await humanloop.prompts.list()); ``` " \ -H "Content-Type: application/json" \ -d '{ "path": "persona", "output_message": { "role": "assistant", "content": "Well, you know, there is so much secrecy involved in government, folks, it\'s unbelievable. They don\'t want to tell you everything. They don\'t tell me everything! But about Roswell, it\'s a very popular question. I know, I just know, that something very, very peculiar happened there. Was it a weather balloon? Maybe. Was it something extraterrestrial? Could be. I\'d love to go down and open up all the classified documents, believe me, I would. But they don\'t let that happen. The Deep State, folks, the Deep State. They\'re unbelievable. They want to keep everything a secret. But whatever the truth is, I can tell you this: it\'s something big, very very big. Tremendous, in fact." }, "prompt_tokens": 100, "output_tokens": 220, "prompt_cost": 0.00001, "output_cost": 0.0002, "finish_reason": "stop", "messages": [ { "role": "user", "content": "What really happened at Roswell?" } ], "prompt": { "model": "gpt-4", "template": [ { "role": "system", "content": "You are {{person}}. Answer questions as this person. Do not break character." } ] }, "created_at": "2024-07-19T00:29:35.178992", "error": null, "provider_latency": 6.5931549072265625, "inputs": { "person": "Trump" } }' ``` ```python Log prompt import datetime from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.log( path="persona", prompt={ "model": "gpt-4", "template": [ { "role": "system", "content": "You are {{person}}. Answer questions as this person. Do not break character.", } ], }, messages=[{"role": "user", "content": "What really happened at Roswell?"}], inputs={"person": "Trump"}, created_at=datetime.datetime.fromisoformat( "2024-07-19 00:29:35.178000+00:00", ), provider_latency=6.5931549072265625, output_message={ "content": "Well, you know, there is so much secrecy involved in government, folks, it's unbelievable. They don't want to tell you everything. They don't tell me everything! But about Roswell, it's a very popular question. I know, I just know, that something very, very peculiar happened there. Was it a weather balloon? Maybe. Was it something extraterrestrial? Could be. I'd love to go down and open up all the classified documents, believe me, I would. But they don't let that happen. The Deep State, folks, the Deep State. They're unbelievable. They want to keep everything a secret. But whatever the truth is, I can tell you this: it's something big, very very big. Tremendous, in fact.", "role": "assistant", }, prompt_tokens=100, output_tokens=220, prompt_cost=1e-05, output_cost=0.0002, finish_reason="stop", ) ``` ```typescript Log prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.log({ path: "persona", prompt: { model: "gpt-4", template: [{ role: "system", content: "You are {{person}}. Answer questions as this person. Do not break character." }] }, messages: [{ role: "user", content: "What really happened at Roswell?" }], inputs: { "person": "Trump" }, createdAt: "2024-07-19T00:29:35.178992", error: undefined, providerLatency: 6.5931549072265625, outputMessage: { content: "Well, you know, there is so much secrecy involved in government, folks, it's unbelievable. They don't want to tell you everything. They don't tell me everything! But about Roswell, it's a very popular question. I know, I just know, that something very, very peculiar happened there. Was it a weather balloon? Maybe. Was it something extraterrestrial? Could be. I'd love to go down and open up all the classified documents, believe me, I would. But they don't let that happen. The Deep State, folks, the Deep State. They're unbelievable. They want to keep everything a secret. But whatever the truth is, I can tell you this: it's something big, very very big. Tremendous, in fact.", role: "assistant" }, promptTokens: 100, outputTokens: 220, promptCost: 0.00001, outputCost: 0.0002, finishReason: "stop" }); ``` ```go Log prompt package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/log" payload := strings.NewReader("{\n \"path\": \"persona\",\n \"prompt_tokens\": 100,\n \"output_tokens\": 220,\n \"prompt_cost\": 0.00001,\n \"output_cost\": 0.0002,\n \"finish_reason\": \"stop\",\n \"created_at\": \"2024-07-19T00:29:35.178992\",\n \"error\": null,\n \"provider_latency\": 6.5931549072265625\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Log prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/log") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"path\": \"persona\",\n \"prompt_tokens\": 100,\n \"output_tokens\": 220,\n \"prompt_cost\": 0.00001,\n \"output_cost\": 0.0002,\n \"finish_reason\": \"stop\",\n \"created_at\": \"2024-07-19T00:29:35.178992\",\n \"error\": null,\n \"provider_latency\": 6.5931549072265625\n}" response = http.request(request) puts response.read_body ``` ```java Log prompt HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/log") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"path\": \"persona\",\n \"prompt_tokens\": 100,\n \"output_tokens\": 220,\n \"prompt_cost\": 0.00001,\n \"output_cost\": 0.0002,\n \"finish_reason\": \"stop\",\n \"created_at\": \"2024-07-19T00:29:35.178992\",\n \"error\": null,\n \"provider_latency\": 6.5931549072265625\n}") .asString(); ``` ```php Log prompt request('POST', 'https://api.humanloop.com/v5/prompts/log', [ 'body' => '{ "path": "persona", "prompt_tokens": 100, "output_tokens": 220, "prompt_cost": 0.00001, "output_cost": 0.0002, "finish_reason": "stop", "created_at": "2024-07-19T00:29:35.178992", "error": null, "provider_latency": 6.5931549072265625 }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Log prompt var client = new RestClient("https://api.humanloop.com/v5/prompts/log"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"path\": \"persona\",\n \"prompt_tokens\": 100,\n \"output_tokens\": 220,\n \"prompt_cost\": 0.00001,\n \"output_cost\": 0.0002,\n \"finish_reason\": \"stop\",\n \"created_at\": \"2024-07-19T00:29:35.178992\",\n \"error\": null,\n \"provider_latency\": 6.5931549072265625\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Log prompt import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [ "path": "persona", "prompt_tokens": 100, "output_tokens": 220, "prompt_cost": 0.00001, "output_cost": 0.0002, "finish_reason": "stop", "created_at": "2024-07-19T00:29:35.178992", "error": , "provider_latency": 6.5931549072265625 ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/log")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X POST "https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string" \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python import datetime from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.log( path="persona", prompt={ "model": "gpt-4", "template": [ { "role": "system", "content": "You are {{person}}. Answer questions as this person. Do not break character.", } ], }, messages=[{"role": "user", "content": "What really happened at Roswell?"}], inputs={"person": "Trump"}, created_at=datetime.datetime.fromisoformat( "2024-07-19 00:29:35.178000+00:00", ), provider_latency=6.5931549072265625, output_message={ "content": "Well, you know, there is so much secrecy involved in government, folks, it's unbelievable. They don't want to tell you everything. They don't tell me everything! But about Roswell, it's a very popular question. I know, I just know, that something very, very peculiar happened there. Was it a weather balloon? Maybe. Was it something extraterrestrial? Could be. I'd love to go down and open up all the classified documents, believe me, I would. But they don't let that happen. The Deep State, folks, the Deep State. They're unbelievable. They want to keep everything a secret. But whatever the truth is, I can tell you this: it's something big, very very big. Tremendous, in fact.", "role": "assistant", }, prompt_tokens=100, output_tokens=220, prompt_cost=1e-05, output_cost=0.0002, finish_reason="stop", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.log({ path: "persona", prompt: { model: "gpt-4", template: [{ role: "system", content: "You are {{person}}. Answer questions as this person. Do not break character." }] }, messages: [{ role: "user", content: "What really happened at Roswell?" }], inputs: { "person": "Trump" }, createdAt: "2024-07-19T00:29:35.178992", error: undefined, providerLatency: 6.5931549072265625, outputMessage: { content: "Well, you know, there is so much secrecy involved in government, folks, it's unbelievable. They don't want to tell you everything. They don't tell me everything! But about Roswell, it's a very popular question. I know, I just know, that something very, very peculiar happened there. Was it a weather balloon? Maybe. Was it something extraterrestrial? Could be. I'd love to go down and open up all the classified documents, believe me, I would. But they don't let that happen. The Deep State, folks, the Deep State. They're unbelievable. They want to keep everything a secret. But whatever the truth is, I can tell you this: it's something big, very very big. Tremendous, in fact.", role: "assistant" }, promptTokens: 100, outputTokens: 220, promptCost: 0.00001, outputCost: 0.0002, finishReason: "stop" }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string" payload := strings.NewReader("{}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/log?version_id=string&environment=string")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Update Prompt Log ```http PATCH https://api.humanloop.com/v5/prompts/{id}/log/{log_id} Content-Type: application/json ``` Update a Log. Update the details of a Log with the given ID. ## Path Parameters - Id (required): Unique identifier for Prompt. - LogId (required): Unique identifier for the Log. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell curl -X PATCH https://api.humanloop.com/v5/prompts/id/log/log_id \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.update_log( id="id", log_id="log_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.updateLog("id", "log_id"); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/log/log_id" payload := strings.NewReader("{}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/log/log_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/id/log/log_id") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('PATCH', 'https://api.humanloop.com/v5/prompts/id/log/log_id', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/log/log_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/log/log_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X PATCH https://api.humanloop.com/v5/prompts/:id/log/:log_id \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.update_log( id="id", log_id="log_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.updateLog("id", "log_id"); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id" payload := strings.NewReader("{}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('PATCH', 'https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/log/%3Alog_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Call Prompt ```http POST https://api.humanloop.com/v5/prompts/call Content-Type: application/json ``` Call a Prompt. Calling a Prompt calls the model provider before logging the request, responses and metadata to Humanloop. You can use query parameters `version_id`, or `environment`, to target an existing version of the Prompt. Otherwise the default deployed version will be chosen. Instead of targeting an existing version explicitly, you can instead pass in Prompt details in the request body. In this case, we will check if the details correspond to an existing version of the Prompt. If they do not, we will create a new version. This is helpful in the case where you are storing or deriving your Prompt details in code. ## Query Parameters - VersionId (optional): A specific Version ID of the Prompt to log to. - Environment (optional): Name of the Environment identifying a deployed version to log to. ## Response Body - 200: - 422: Validation Error ## Examples ```shell Supplying Prompt with Tool curl -X POST https://api.humanloop.com/v5/prompts/call \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "stream": false, "path": "persona", "messages": [ { "role": "user", "content": "latest apple" } ], "prompt": { "model": "gpt-4", "template": [ { "role": "system", "content": "You are stockbot. Return latest prices." } ], "tools": [ { "name": "get_stock_price", "description": "Get current stock price", "parameters": { "type": "object", "properties": { "ticker_symbol": { "type": "string", "name": "Ticker Symbol", "description": "Ticker symbol of the stock" } }, "required": [] } } ] } }' ``` ```python Supplying Prompt with Tool from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.call( path="persona", prompt={ "model": "gpt-4", "template": [ { "role": "system", "content": "You are stockbot. Return latest prices.", } ], "tools": [ { "name": "get_stock_price", "description": "Get current stock price", "parameters": { "type": "object", "properties": { "ticker_symbol": { "type": "string", "name": "Ticker Symbol", "description": "Ticker symbol of the stock", } }, "required": [], }, } ], }, messages=[{"role": "user", "content": "latest apple"}], ) ``` ```typescript Supplying Prompt with Tool import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.call({ path: "persona", prompt: { model: "gpt-4", template: [{ role: "system", content: "You are stockbot. Return latest prices." }], tools: [{ name: "get_stock_price", description: "Get current stock price", parameters: { "type": "object", "properties": { "ticker_symbol": { "type": "string", "name": "Ticker Symbol", "description": "Ticker symbol of the stock" } }, "required": [] } }] }, messages: [{ role: "user", content: "latest apple" }] }); ``` ```go Supplying Prompt with Tool package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/call" payload := strings.NewReader("{\n \"stream\": false,\n \"path\": \"persona\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Supplying Prompt with Tool require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/call") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"stream\": false,\n \"path\": \"persona\"\n}" response = http.request(request) puts response.read_body ``` ```java Supplying Prompt with Tool HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/call") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"stream\": false,\n \"path\": \"persona\"\n}") .asString(); ``` ```php Supplying Prompt with Tool request('POST', 'https://api.humanloop.com/v5/prompts/call', [ 'body' => '{ "stream": false, "path": "persona" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Supplying Prompt with Tool var client = new RestClient("https://api.humanloop.com/v5/prompts/call"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"stream\": false,\n \"path\": \"persona\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Supplying Prompt with Tool import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [ "stream": false, "path": "persona" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/call")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell Supplying Prompt curl -X POST https://api.humanloop.com/v5/prompts/call \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "stream": false, "path": "persona", "messages": [ { "role": "user", "content": "What really happened at Roswell?" } ], "prompt": { "model": "gpt-4", "template": [ { "role": "system", "content": "You are {{person}}. Answer any questions as this person. Do not break character." } ] }, "inputs": { "person": "Trump" } }' ``` ```python Supplying Prompt from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.call( path="persona", prompt={ "model": "gpt-4", "template": [ { "role": "system", "content": "You are {{person}}. Answer any questions as this person. Do not break character.", } ], }, messages=[{"role": "user", "content": "What really happened at Roswell?"}], inputs={"person": "Trump"}, ) ``` ```typescript Supplying Prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.call({ path: "persona", prompt: { model: "gpt-4", template: [{ role: "system", content: "You are {{person}}. Answer any questions as this person. Do not break character." }] }, messages: [{ role: "user", content: "What really happened at Roswell?" }], inputs: { "person": "Trump" } }); ``` ```go Supplying Prompt package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/call" payload := strings.NewReader("{\n \"stream\": false,\n \"path\": \"persona\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Supplying Prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/call") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"stream\": false,\n \"path\": \"persona\"\n}" response = http.request(request) puts response.read_body ``` ```java Supplying Prompt HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/call") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"stream\": false,\n \"path\": \"persona\"\n}") .asString(); ``` ```php Supplying Prompt request('POST', 'https://api.humanloop.com/v5/prompts/call', [ 'body' => '{ "stream": false, "path": "persona" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Supplying Prompt var client = new RestClient("https://api.humanloop.com/v5/prompts/call"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"stream\": false,\n \"path\": \"persona\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Supplying Prompt import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [ "stream": false, "path": "persona" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/call")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell By ID curl -X POST "https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk" \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "path": "persona", "messages": [ { "role": "user", "content": "What really happened at Roswell?" } ], "inputs": { "person": "Trump" } }' ``` ```python By ID from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.call( version_id="prv_Wu6zx1lAWJRqOyL8nWuZk", path="persona", messages=[{"role": "user", "content": "What really happened at Roswell?"}], inputs={"person": "Trump"}, ) ``` ```typescript By ID import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.call({ versionId: "prv_Wu6zx1lAWJRqOyL8nWuZk", path: "persona", messages: [{ role: "user", content: "What really happened at Roswell?" }], inputs: { "person": "Trump" } }); ``` ```go By ID package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk" payload := strings.NewReader("{\n \"path\": \"persona\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby By ID require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"path\": \"persona\"\n}" response = http.request(request) puts response.read_body ``` ```java By ID HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"path\": \"persona\"\n}") .asString(); ``` ```php By ID request('POST', 'https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk', [ 'body' => '{ "path": "persona" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp By ID var client = new RestClient("https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"path\": \"persona\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift By ID import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = ["path": "persona"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/call?version_id=prv_Wu6zx1lAWJRqOyL8nWuZk")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X POST "https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string" \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "stream": false }' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.call( path="persona", prompt={ "model": "gpt-4", "template": [ { "role": "system", "content": "You are stockbot. Return latest prices.", } ], "tools": [ { "name": "get_stock_price", "description": "Get current stock price", "parameters": { "type": "object", "properties": { "ticker_symbol": { "type": "string", "name": "Ticker Symbol", "description": "Ticker symbol of the stock", } }, "required": [], }, } ], }, messages=[{"role": "user", "content": "latest apple"}], ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.call({ path: "persona", prompt: { model: "gpt-4", template: [{ role: "system", content: "You are stockbot. Return latest prices." }], tools: [{ name: "get_stock_price", description: "Get current stock price", parameters: { "type": "object", "properties": { "ticker_symbol": { "type": "string", "name": "Ticker Symbol", "description": "Ticker symbol of the stock" } }, "required": [] } }] }, messages: [{ role: "user", content: "latest apple" }] }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string" payload := strings.NewReader("{\n \"stream\": false\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"stream\": false\n}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"stream\": false\n}") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string', [ 'body' => '{ "stream": false }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"stream\": false\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = ["stream": false] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/call?version_id=string&environment=string")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List Prompts ```http GET https://api.humanloop.com/v5/prompts ``` Get a list of all Prompts. ## Query Parameters - Page (optional): Page number for pagination. - Size (optional): Page size for pagination. Number of Prompts to fetch. - Name (optional): Case-insensitive filter for Prompt name. - UserFilter (optional): Case-insensitive filter for users in the Prompt. This filter matches against both email address and name of users. - SortBy (optional): Field to sort Prompts by - Order (optional): Direction to sort by. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell curl -G https://api.humanloop.com/v5/prompts \ -H "X-API-KEY: " \ -d size=1 ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) response = client.prompts.list( size=1, ) for item in response: yield item # alternatively, you can paginate page-by-page for page in response.iter_pages(): yield page ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); const response = await client.prompts.list({ size: 1 }); for await (const item of response) { console.log(item); } // Or you can manually iterate page-by-page const page = await client.prompts.list({ size: 1 }); while (page.hasNextPage()) { page = page.getNextPage(); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts?size=1" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts?size=1") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts?size=1") .header("X-API-KEY", " ") .asString(); ``` ```php request('GET', 'https://api.humanloop.com/v5/prompts?size=1', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts?size=1"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts?size=1")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -G https://api.humanloop.com/v5/prompts \ -H "X-API-KEY: " \ -d page=0 \ -d size=0 ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) response = client.prompts.list( size=1, ) for item in response: yield item # alternatively, you can paginate page-by-page for page in response.iter_pages(): yield page ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); const response = await client.prompts.list({ size: 1 }); for await (const item of response) { console.log(item); } // Or you can manually iterate page-by-page const page = await client.prompts.list({ size: 1 }); while (page.hasNextPage()) { page = page.getNextPage(); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts?page=0&size=0" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts?page=0&size=0") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts?page=0&size=0") .header("X-API-KEY", " ") .asString(); ``` ```php request('GET', 'https://api.humanloop.com/v5/prompts?page=0&size=0', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts?page=0&size=0"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts?page=0&size=0")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Upsert Prompt ```http POST https://api.humanloop.com/v5/prompts Content-Type: application/json ``` Create a Prompt or update it with a new version if it already exists. Prompts are identified by the `ID` or their `path`. The parameters (i.e. the prompt template, temperature, model etc.) determine the versions of the Prompt. You can provide `version_name` and `version_description` to identify and describe your versions. Version names must be unique within a Prompt - attempting to create a version with a name that already exists will result in a 409 Conflict error. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell Upsert prompt curl -X POST https://api.humanloop.com/v5/prompts \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "path": "Personal Projects/Coding Assistant", "endpoint": "chat", "template": [ { "content": "You are a helpful coding assistant specialising in {{language}}", "role": "system" } ], "provider": "openai", "max_tokens": -1, "temperature": 0.7 }' ``` ```python Upsert prompt from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.upsert( path="Personal Projects/Coding Assistant", model="gpt-4o", endpoint="chat", template=[ { "content": "You are a helpful coding assistant specialising in {{language}}", "role": "system", } ], provider="openai", max_tokens=-1, temperature=0.7, ) ``` ```typescript Upsert prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.upsert({ path: "Personal Projects/Coding Assistant", model: "gpt-4o", endpoint: "chat", template: [{ content: "You are a helpful coding assistant specialising in {{language}}", role: "system" }], provider: "openai", maxTokens: -1, temperature: 0.7, commitMessage: "Initial commit" }); ``` ```go Upsert prompt package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts" payload := strings.NewReader("{\n \"model\": \"gpt-4o\",\n \"path\": \"Personal Projects/Coding Assistant\",\n \"endpoint\": \"chat\",\n \"provider\": \"openai\",\n \"max_tokens\": -1,\n \"temperature\": 0.7\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Upsert prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"model\": \"gpt-4o\",\n \"path\": \"Personal Projects/Coding Assistant\",\n \"endpoint\": \"chat\",\n \"provider\": \"openai\",\n \"max_tokens\": -1,\n \"temperature\": 0.7\n}" response = http.request(request) puts response.read_body ``` ```java Upsert prompt HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"model\": \"gpt-4o\",\n \"path\": \"Personal Projects/Coding Assistant\",\n \"endpoint\": \"chat\",\n \"provider\": \"openai\",\n \"max_tokens\": -1,\n \"temperature\": 0.7\n}") .asString(); ``` ```php Upsert prompt request('POST', 'https://api.humanloop.com/v5/prompts', [ 'body' => '{ "model": "gpt-4o", "path": "Personal Projects/Coding Assistant", "endpoint": "chat", "provider": "openai", "max_tokens": -1, "temperature": 0.7 }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Upsert prompt var client = new RestClient("https://api.humanloop.com/v5/prompts"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"model\": \"gpt-4o\",\n \"path\": \"Personal Projects/Coding Assistant\",\n \"endpoint\": \"chat\",\n \"provider\": \"openai\",\n \"max_tokens\": -1,\n \"temperature\": 0.7\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Upsert prompt import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [ "model": "gpt-4o", "path": "Personal Projects/Coding Assistant", "endpoint": "chat", "provider": "openai", "max_tokens": -1, "temperature": 0.7 ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X POST https://api.humanloop.com/v5/prompts \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "model": "string" }' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.upsert( path="Personal Projects/Coding Assistant", model="gpt-4o", endpoint="chat", template=[ { "content": "You are a helpful coding assistant specialising in {{language}}", "role": "system", } ], provider="openai", max_tokens=-1, temperature=0.7, ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.upsert({ path: "Personal Projects/Coding Assistant", model: "gpt-4o", endpoint: "chat", template: [{ content: "You are a helpful coding assistant specialising in {{language}}", role: "system" }], provider: "openai", maxTokens: -1, temperature: 0.7, commitMessage: "Initial commit" }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts" payload := strings.NewReader("{\n \"model\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"model\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"model\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts', [ 'body' => '{ "model": "string" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"model\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = ["model": "string"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Prompt ```http GET https://api.humanloop.com/v5/prompts/{id} ``` Retrieve the Prompt with the given ID. By default, the deployed version of the Prompt is returned. Use the query parameters `version_id` or `environment` to target a specific version of the Prompt. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Query Parameters - VersionId (optional): A specific Version ID of the Prompt to retrieve. - Environment (optional): Name of the Environment to retrieve a deployed Version from. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell Get specific prompt curl https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa \ -H "X-API-KEY: " ``` ```python Get specific prompt from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.get( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript Get specific prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.get("pr_30gco7dx6JDq4200GVOHa"); ``` ```go Get specific prompt package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Get specific prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java Get specific prompt HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") .header("X-API-KEY", " ") .asString(); ``` ```php Get specific prompt request('GET', 'https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Get specific prompt var client = new RestClient("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift Get specific prompt import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -G https://api.humanloop.com/v5/prompts/:id \ -H "X-API-KEY: " \ -d version_id=string \ -d environment=string ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.get( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.get("pr_30gco7dx6JDq4200GVOHa"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string") .header("X-API-KEY", " ") .asString(); ``` ```php request('GET', 'https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid?version_id=string&environment=string")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Prompt ```http DELETE https://api.humanloop.com/v5/prompts/{id} ``` Delete the Prompt with the given ID. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Response Body - 422: Validation Error ## Examples ```shell Delete prompt curl -X DELETE https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa \ -H "X-API-KEY: " ``` ```python Delete prompt from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.delete( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript Delete prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.delete("pr_30gco7dx6JDq4200GVOHa"); ``` ```go Delete prompt package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Delete prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java Delete prompt HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") .header("X-API-KEY", " ") .asString(); ``` ```php Delete prompt request('DELETE', 'https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Delete prompt var client = new RestClient("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift Delete prompt import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X DELETE https://api.humanloop.com/v5/prompts/:id \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.delete( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.delete("pr_30gco7dx6JDq4200GVOHa"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/%3Aid") .header("X-API-KEY", " ") .asString(); ``` ```php request('DELETE', 'https://api.humanloop.com/v5/prompts/%3Aid', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Move Prompt ```http PATCH https://api.humanloop.com/v5/prompts/{id} Content-Type: application/json ``` Move the Prompt to a different path or change the name. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell Move prompt curl -X PATCH https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "path": "new directory/new name" }' ``` ```python Move prompt from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.move( id="pr_30gco7dx6JDq4200GVOHa", path="new directory/new name", ) ``` ```typescript Move prompt import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.move("pr_30gco7dx6JDq4200GVOHa", { path: "new directory/new name" }); ``` ```go Move prompt package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa" payload := strings.NewReader("{\n \"path\": \"new directory/new name\"\n}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Move prompt require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"path\": \"new directory/new name\"\n}" response = http.request(request) puts response.read_body ``` ```java Move prompt HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"path\": \"new directory/new name\"\n}") .asString(); ``` ```php Move prompt request('PATCH', 'https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa', [ 'body' => '{ "path": "new directory/new name" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp Move prompt var client = new RestClient("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"path\": \"new directory/new name\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Move prompt import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = ["path": "new directory/new name"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X PATCH https://api.humanloop.com/v5/prompts/:id \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.move( id="pr_30gco7dx6JDq4200GVOHa", path="new directory/new name", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.move("pr_30gco7dx6JDq4200GVOHa", { path: "new directory/new name" }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid" payload := strings.NewReader("{}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/%3Aid") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('PATCH', 'https://api.humanloop.com/v5/prompts/%3Aid', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Populate Prompt template ```http POST https://api.humanloop.com/v5/prompts/{id}/populate Content-Type: application/json ``` Retrieve the Prompt with the given ID, including the populated template. By default, the deployed version of the Prompt is returned. Use the query parameters `version_id` or `environment` to target a specific version of the Prompt. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Query Parameters - VersionId (optional): A specific Version ID of the Prompt to retrieve to populate the template. - Environment (optional): Name of the Environment to retrieve a deployed Version from to populate the template. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell curl -X POST https://api.humanloop.com/v5/prompts/id/populate \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "key": "value" }' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.populate( id="id", request={"key": "value"}, ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.populateTemplate("id", { body: { "key": "value" } }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/populate" payload := strings.NewReader("{\n \"key\": \"value\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/populate") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{\n \"key\": \"value\"\n}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/id/populate") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{\n \"key\": \"value\"\n}") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/id/populate', [ 'body' => '{ "key": "value" }', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/populate"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"key\": \"value\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = ["key": "value"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/populate")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X POST "https://api.humanloop.com/v5/prompts/:id/populate?version_id=string&environment=string" \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{ "string": {} }' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.populate( id="id", request={"key": "value"}, ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.populateTemplate("id", { body: { "key": "value" } }); ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string" payload := strings.NewReader("{}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/populate?version_id=string&environment=string")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List Versions of a Prompt ```http GET https://api.humanloop.com/v5/prompts/{id}/versions ``` Get a list of all the versions of a Prompt. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Query Parameters - EvaluatorAggregates (optional): Whether to include Evaluator aggregate results for the versions in the response ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell List versions curl https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions \ -H "X-API-KEY: " ``` ```python List versions from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.list_versions( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript List versions import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.listVersions("pr_30gco7dx6JDq4200GVOHa", { status: "committed" }); ``` ```go List versions package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby List versions require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java List versions HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions") .header("X-API-KEY", " ") .asString(); ``` ```php List versions request('GET', 'https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp List versions var client = new RestClient("https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift List versions import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/versions")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -G https://api.humanloop.com/v5/prompts/:id/versions \ -H "X-API-KEY: " \ -d evaluator_aggregates=true ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.list_versions( id="pr_30gco7dx6JDq4200GVOHa", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.listVersions("pr_30gco7dx6JDq4200GVOHa", { status: "committed" }); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.get("https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true") .header("X-API-KEY", " ") .asString(); ``` ```php request('GET', 'https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true"); var request = new RestRequest(Method.GET); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/versions?evaluator_aggregates=true")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Prompt Version ```http DELETE https://api.humanloop.com/v5/prompts/{id}/versions/{version_id} ``` Delete a version of the Prompt. ## Path Parameters - Id (required): Unique identifier for Prompt. - VersionId (required): Unique identifier for the specific version of the Prompt. ## Response Body - 422: Validation Error ## Examples ```shell curl -X DELETE https://api.humanloop.com/v5/prompts/id/versions/version_id \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.delete_prompt_version( id="id", version_id="version_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.deletePromptVersion("id", "version_id"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/versions/version_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/versions/version_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/id/versions/version_id") .header("X-API-KEY", " ") .asString(); ``` ```php request('DELETE', 'https://api.humanloop.com/v5/prompts/id/versions/version_id', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/versions/version_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/versions/version_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X DELETE https://api.humanloop.com/v5/prompts/:id/versions/:version_id \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.delete_prompt_version( id="id", version_id="version_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.deletePromptVersion("id", "version_id"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id") .header("X-API-KEY", " ") .asString(); ``` ```php request('DELETE', 'https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Update Prompt Version ```http PATCH https://api.humanloop.com/v5/prompts/{id}/versions/{version_id} Content-Type: application/json ``` Update the name or description of the Prompt version. ## Path Parameters - Id (required): Unique identifier for Prompt. - VersionId (required): Unique identifier for the specific version of the Prompt. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell curl -X PATCH https://api.humanloop.com/v5/prompts/id/versions/version_id \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.patch_prompt_version( id="id", version_id="version_id", ) ``` ```javascript const url = 'https://api.humanloop.com/v5/prompts/id/versions/version_id'; const options = { method: 'PATCH', headers: {'X-API-KEY': ' ', 'Content-Type': 'application/json'}, body: '{}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/versions/version_id" payload := strings.NewReader("{}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/versions/version_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/id/versions/version_id") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('PATCH', 'https://api.humanloop.com/v5/prompts/id/versions/version_id', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/versions/version_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/versions/version_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X PATCH https://api.humanloop.com/v5/prompts/:id/versions/:version_id \ -H "X-API-KEY: " \ -H "Content-Type: application/json" \ -d '{}' ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.patch_prompt_version( id="id", version_id="version_id", ) ``` ```javascript const url = 'https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id'; const options = { method: 'PATCH', headers: {'X-API-KEY': ' ', 'Content-Type': 'application/json'}, body: '{}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id" payload := strings.NewReader("{}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("X-API-KEY", " ") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["X-API-KEY"] = ' ' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.patch("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id") .header("X-API-KEY", " ") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('PATCH', 'https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id', [ 'body' => '{}', 'headers' => [ 'Content-Type' => 'application/json', 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("X-API-KEY", " "); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "X-API-KEY": " ", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/versions/%3Aversion_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Deploy Prompt ```http POST https://api.humanloop.com/v5/prompts/{id}/environments/{environment_id} ``` Deploy Prompt to an Environment. Set the deployed version for the specified Environment. This Prompt will be used for calls made to the Prompt in this Environment. ## Path Parameters - Id (required): Unique identifier for Prompt. - EnvironmentId (required): Unique identifier for the Environment to deploy the Version to. ## Query Parameters - VersionId (required): Unique identifier for the specific version of the Prompt. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell curl -X POST "https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id" \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.set_deployment( id="id", environment_id="environment_id", version_id="version_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.setDeployment("id", "environment_id", { versionId: "version_id" }); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id" req, _ := http.NewRequest("POST", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id") .header("X-API-KEY", " ") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/environments/environment_id?version_id=version_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X POST "https://api.humanloop.com/v5/prompts/:id/environments/:environment_id?version_id=string" \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.set_deployment( id="id", environment_id="environment_id", version_id="version_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.setDeployment("id", "environment_id", { versionId: "version_id" }); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string" req, _ := http.NewRequest("POST", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.post("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string") .header("X-API-KEY", " ") .asString(); ``` ```php request('POST', 'https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string"); var request = new RestRequest(Method.POST); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id?version_id=string")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Remove Deployment ```http DELETE https://api.humanloop.com/v5/prompts/{id}/environments/{environment_id} ``` Remove deployed Prompt from the Environment. Remove the deployed version for the specified Environment. This Prompt will no longer be used for calls made to the Prompt in this Environment. ## Path Parameters - Id (required): Unique identifier for Prompt. - EnvironmentId (required): Unique identifier for the Environment to remove the deployment from. ## Response Body - 422: Validation Error ## Examples ```shell curl -X DELETE https://api.humanloop.com/v5/prompts/id/environments/environment_id \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.remove_deployment( id="id", environment_id="environment_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.removeDeployment("id", "environment_id"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/id/environments/environment_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/id/environments/environment_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/id/environments/environment_id") .header("X-API-KEY", " ") .asString(); ``` ```php request('DELETE', 'https://api.humanloop.com/v5/prompts/id/environments/environment_id', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/id/environments/environment_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/id/environments/environment_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ```shell curl -X DELETE https://api.humanloop.com/v5/prompts/:id/environments/:environment_id \ -H "X-API-KEY: " ``` ```python from humanloop import Humanloop client = Humanloop( api_key="YOUR_API_KEY", ) client.prompts.remove_deployment( id="id", environment_id="environment_id", ) ``` ```typescript import { HumanloopClient } from "humanloop"; const client = new HumanloopClient({ apiKey: "YOUR_API_KEY" }); await client.prompts.removeDeployment("id", "environment_id"); ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("X-API-KEY", " ") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["X-API-KEY"] = ' ' response = http.request(request) puts response.read_body ``` ```java HttpResponse response = Unirest.delete("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id") .header("X-API-KEY", " ") .asString(); ``` ```php request('DELETE', 'https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id', [ 'headers' => [ 'X-API-KEY' => ' ', ], ]); echo $response->getBody(); ``` ```csharp var client = new RestClient("https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("X-API-KEY", " "); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["X-API-KEY": " "] let request = NSMutableURLRequest(url: NSURL(string: "https://api.humanloop.com/v5/prompts/%3Aid/environments/%3Aenvironment_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List a Prompt's Environments ```http GET https://api.humanloop.com/v5/prompts/{id}/environments ``` List all Environments and their deployed versions for the Prompt. ## Path Parameters - Id (required): Unique identifier for Prompt. ## Response Body - 200: Successful Response - 422: Validation Error ## Examples ```shell List environments curl https://api.humanloop.com/v5/prompts/pr_30gco7dx6JDq4200GVOHa/environments \ -H "X-API-KEY: