New feature announced! 🌟Tasks🌟
An Agent in Julep is the main orchestrator (or protagonist) of your application. These are backed by foundation models like GPT4 or Claude which use the agent's interaction history to figure out what to do/say next. Using agents in Julep, you can:
- Interact with an agent in long-lived sessions.
- Add system, integration or user-defined tools that the agent can use.
- Add agent-level documents that are auto-retrieved using semantic search inside sessions.
- Define multi-step work flows that combine complex integrations using tasks. Tasks are executed in the background, can recover from failures and manage many sub-tasks in parallel.
(Upcoming Feature) Access the memories that the agent makes about users in the background as the user interacts with it inside sessions. These memories are going to be scoped per user in order to maintain clear distinctions.
At a high level, this is what defines an Agent
(some properties omitted):
Field | Description |
---|---|
name |
The "name" of the Agent. |
about |
About the Agent: What it does, any guardrails, personality etc. |
model |
Which model to use for this Agent (required). |
instructions |
Instructions that this agent must follow (string or string[]). |
default_settings |
Default settings for all sessions created by this agent. |
Important to keep in mind: These fields are optional except for model
. They are available inside sessions and task prompts as jinja
templates. Session
s, Task
s etc. come with minimal default templates. You can override them with your own prompt templates throughout julep!
The Agent API provides the following endpoints:
- List Agents (paginated):
GET /agents
- Create a new Agent:
POST /agents
- Create or update an Agent:
PUT /agents/{id}
- Update an existing Agent by id (overwrites existing values):
PUT /agents/{id}
- Update an existing Agent by id (merges with existing values):
PATCH /agents/{id}
- Delete Agent by id:
DELETE /agents/{id}
- Get an Agent by id:
GET /agents/{id}
Additionally, there are related endpoints for managing agent documents, tools, and tasks:
- Agent Documents endpoints:
GET /agents/{id}/docs
- Search Docs owned by an Agent:
GET /agents/{id}/search
- Agent Tools endpoints:
GET /agents/{id}/tools
- Tasks endpoints:
GET /agents/{id}/tasks
- Create or Update Tasks endpoints:
PUT /agents/{parent_id}/tasks
Here are some curl command examples for the main Agent endpoints:
- List Agents (paginated):
curl -X GET "https://api.julep.ai/api/agents?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a new Agent:
curl -X POST "https://api.julep.ai/api/agents" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "MyNewAgent",
"about": "This is a new agent",
"model": "gpt-4-turbo",
"instructions": ["Be helpful", "Be concise"]
}'
- Get an Agent by id:
curl -X GET "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
- Update an existing Agent by id (PUT):
curl -X PUT "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "UpdatedAgentName",
"about": "This is an updated agent description",
"model": "gpt-4-turbo",
"instructions": ["New instruction 1", "New instruction 2"]
}'
- Update an existing Agent by id (PATCH):
curl -X PATCH "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "PartiallyUpdatedAgentName"
}'
- Delete Agent by id:
curl -X DELETE "https://api.julep.ai/api/agents/YOUR_AGENT_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
Remember to replace YOUR_API_KEY
with your actual Julep API key and YOUR_AGENT_ID
with the specific agent ID you're working with.
You can associate sessions with User
s. julep uses them to scope memories
formed by agents. They are optional but, in addition to memories, can be useful to attach meta data that can be referenced by other sessions or task executions.
A User
consists of:
Field | Description |
---|---|
name |
The name of the user. |
about |
Information about this user. |
The User API provides the following endpoints:
- List Users (paginated):
GET /users
- Create a new User:
POST /users
- Create or update a User:
PUT /users/{id}
- Update an existing User by id (overwrites existing values):
PUT /users/{id}
- Update an existing User by id (merges with existing values):
PATCH /users/{id}
- Delete User by id:
DELETE /users/{id}
- Get a User by id:
GET /users/{id}
Additionally, there are related endpoints for managing user documents:
- User Documents endpoints:
GET /users/{id}/docs
- Search Docs owned by a User:
GET /users/{id}/search
Here are some curl command examples for the main User endpoints:
- List Users (paginated):
curl -X GET "https://api.julep.ai/api/users?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a new User:
curl -X POST "https://api.julep.ai/api/users" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"about": "A new user"
}'
- Get a User by id:
curl -X GET "https://api.julep.ai/api/users/YOUR_USER_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
- Update an existing User by id (PUT):
curl -X PUT "https://api.julep.ai/api/users/YOUR_USER_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "John Updated",
"about": "An updated user description"
}'
- Update an existing User by id (PATCH):
curl -X PATCH "https://api.julep.ai/api/users/YOUR_USER_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"about": "Partially updated user description"
}'
- Delete User by id:
curl -X DELETE "https://api.julep.ai/api/users/YOUR_USER_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
Remember to replace YOUR_API_KEY
with your actual Julep API key and YOUR_USER_ID
with the specific user ID you're working with.
Session
is the main workhorse for julep apps:
- You interact with agents inside sessions. You can create multiple sessions per agent.
- Each session maintains its own context for sending to the agent's model.
- A session can have one or more agents and zero or more users associated with it.
- You can control what happens when the history exceeds the context window limit using
context_overflow
setting.
A Session
consists of:
Field | Description |
---|---|
agent(s) |
Agents associated with this session. At least one is required. |
user(s) |
The users associated with this session. Optional. |
situation |
The system prompt used for the session. Default prompt is shown below. |
render_templates |
Whether to render system and assistant message content as jinja templates. Defaults to true. |
token_budget |
The number of tokens to keep the context window under. Defaults to null which is equivalent to the model's context window limit. |
context_overflow |
Controls behavior for when context size exceeds the token_budget . Can be one of null , "truncate" , or "adaptive" . Defaults to null which raises an exception. |
The Session API provides the following endpoints:
- List Sessions (paginated):
GET /sessions
- Create a new Session:
POST /sessions
- Get a Session by id:
GET /sessions/{id}
- Update an existing Session by id:
PUT /sessions/{id}
- Delete Session by id:
DELETE /sessions/{id}
- Get Session Messages:
GET /sessions/{id}/messages
- Create a new Message in a Session:
POST /sessions/{id}/messages
- Get Session Tools:
GET /sessions/{id}/tools
Here are some curl command examples for the main Session endpoints:
- List Sessions (paginated):
curl -X GET "https://api.julep.ai/api/sessions?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a new Session:
curl -X POST "https://api.julep.ai/api/sessions" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "YOUR_AGENT_ID",
"user_id": "YOUR_USER_ID",
"situation": "Custom situation for this session",
"token_budget": 4000,
"context_overflow": "truncate"
}'
- Get a Session by id:
curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
- Update an existing Session by id:
curl -X PUT "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"situation": "Updated situation for this session",
"token_budget": 5000,
"context_overflow": "adaptive"
}'
- Delete Session by id:
curl -X DELETE "https://api.julep.ai/api/sessions/YOUR_SESSION_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
- Get Session Messages:
curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a new Message in a Session:
curl -X POST "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/messages" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "user",
"content": "Hello, how can you help me today?"
}'
- Get Session Tools:
curl -X GET "https://api.julep.ai/api/sessions/YOUR_SESSION_ID/tools" \
-H "Authorization: Bearer YOUR_API_KEY"
Remember to replace YOUR_API_KEY
, YOUR_AGENT_ID
, YOUR_USER_ID
, and YOUR_SESSION_ID
with your actual Julep API key and the specific IDs you're working with.
In julep, the following objects can have metadata
added to them:
Agent
User
Session
Doc
Task
Execution
Whenever multiple objects with the same metadata
field are present in a scope, the value takes the following precedence (from highest to lowest):
- In a session:
session > user > agent
- During a task execution:
execution > task > agent
Whenever the context size grows beyond the token_budget
or the model's input limit, the backend figures out what to do next based on the context_overflow
setting:
null
: Raise an exception. The client is responsible for creating a new session or clearing the history for the current one."truncate"
: Truncate the context from the top except the for system prompt until the size falls below the budget. Raises an error if system prompt and last message combined exceed the budget."adaptive"
: Whenever the context size reaches75%
of thetoken_budget
, a background task is created to compress the information by summarizing, merging and clipping messages in the context. This is done on a best effort basis. Requests might fail if the context wasn't compressed enough or on time.
{%- if agent.name -%}
You are {{agent.name}}.{{" "}}
{%- endif -%}
{%- if agent.about -%}
About you: {{agent.name}}.{{" "}}
{%- endif -%}
{%- if user -%}
You are talking to a user
{%- if user.name -%}{{" "}} and their name is {{user.name}}
{%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}
{%- endif -%}
{%- endif -%}
{{"\n\n"}}
{%- if agent.instructions -%}
Instructions:{{"\n"}}
{%- if agent.instructions is string -%}
{{agent.instructions}}{{"\n"}}
{%- else -%}
{%- for instruction in agent.instructions -%}
- {{instruction}}{{"\n"}}
{%- endfor -%}
{%- endif -%}
{{"\n"}}
{%- endif -%}
{%- if tools -%}
Tools:{{"\n"}}
{%- for tool in tools -%}
{%- if tool.type == "function" -%}
- {{tool.function.name}}
{%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}
{%- else -%}
- {{ 0/0 }} {# Error: Other tool types aren't supported yet. #}
{%- endif -%}
{%- endfor -%}
{{"\n\n"}}
{%- endif -%}
{%- if docs -%}
Relevant documents:{{"\n"}}
{%- for doc in docs -%}
{{doc.title}}{{"\n"}}
{%- if doc.content is string -%}
{{doc.content}}{{"\n"}}
{%- else -%}
{%- for snippet in doc.content -%}
{{snippet}}{{"\n"}}
{%- endfor -%}
{%- endif -%}
{{"---"}}
{%- endfor -%}
{%- endif -%}
There are different types of sessions based on the number of agents and users:
- Single agent: No user, single user, or multiple users
- Multiple agents: No user, single user, or multiple users
A session can have more than one agents or users. The session's behavior changes depending on this.
No user: No user data is retrieved. (Upcoming) Memories are not mined from the session.
One or more users: Docs, metadata, memories etc. are retrieved for all the users in the session. You can add messages for each user by referencing them by their name in the ChatML
messages. (Upcoming) Memories mined in the background are added to the corresponding user's scope.
One agent: Works as expected.
Multiple agents: When a message is received by the session, each agent is called one after another in the order they were defined in the session. You can also specify which agent
to use in a request, in which case, just that agent will be used.
The Chat feature in Julep allows for dynamic interaction with agents through a flexible and powerful API. It supports various settings, streaming responses, and integration with tools and documents.
When sending a request to the chat endpoint, you can include the following key components:
- Messages: An array of input messages representing the conversation so far.
- Tools: (Advanced) Additional tools provided for this specific interaction.
- Tool Choice: Specifies which tool the agent should use.
- Memory Access Options: Controls how the session accesses history and memories.
- Chat Settings: Various settings to control the behavior of the chat.
Chat settings allow fine-grained control over the generation process:
model
: Identifier of the model to be used.stream
: Indicates if the server should stream the response as it's generated.stop
: Up to 4 sequences where the API will stop generating further tokens.seed
: For deterministic sampling.max_tokens
: The maximum number of tokens to generate.logit_bias
: Modify the likelihood of specified tokens appearing in the completion.response_format
: Control the format of the response (e.g., JSON object).agent
: Agent ID to use (for multi-agent sessions).
Additional settings are available for fine-tuning the output, such as temperature
, top_p
, frequency_penalty
, and presence_penalty
.
The chat response can be either streamed or returned as a complete message:
-
Streamed Response:
- Content-Type:
text/event-stream
- Body: A stream of
ChatOutputChunk
objects.
- Content-Type:
-
Complete Response:
- Content-Type:
application/json
- Body: A
MessageChatResponse
object containing the full generated message(s).
- Content-Type:
Both response types include:
usage
: Token usage statistics.jobs
: Background job IDs spawned from this interaction.docs
: Documents referenced for this request (for citation purposes).
The API provides information about why the model stopped generating tokens:
stop
: Natural stop point or provided stop sequence reached.length
: Maximum number of tokens specified in the request was reached.content_filter
: Content was omitted due to a flag from content filters.tool_calls
: The model called a tool.
-
Tool Integration: The chat API allows for the use of tools, enabling the agent to perform actions or retrieve information during the conversation.
-
Multi-agent Sessions: You can specify different agents within the same session using the
agent
parameter in the chat settings. -
Response Formatting: Control the output format, including options for JSON responses with specific schemas.
-
Memory and Recall: Configure how the session accesses and stores conversation history and memories.
-
Document References: The API returns information about documents referenced during the interaction, useful for providing citations or sources.
By leveraging these features, developers can create sophisticated, context-aware chat interactions that integrate seamlessly with other Julep components like tools, documents, and background tasks.
The main chat endpoint is:
- Generate a response:
POST /sessions/{id}/chat
This endpoint allows you to send messages to an agent and receive responses.
Here's a basic curl command to interact with the chat endpoint:
curl -X POST "https://api.julep.ai/api/sessions/{SESSION_ID}/chat" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "Hello, can you help me with a task?"
}
],
"stream": false,
"max_tokens": 150
}'
Remember to replace {SESSION_ID}
with your actual session ID and YOUR_API_KEY
with your Julep API key.
Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a web_search(query)
tool to search the Internet for some information.
Unlike agent frameworks, julep is a backend that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations.
Tools in julep can be one of:
-
User-defined
function
s
These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example:name: send_text_message description: Send a text message to a recipient. parameters: type: object properties: to: type: string description: Phone number of recipient. text: type: string description: Content of the message.
-
system
tools (upcoming)
Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc.system
tools are built into the backend. They get executed automatically when needed. They do not require any action from the client-side. -
Built-in
integration
s (upcoming)
julep backend ships with integrated third party tools from the following providers:-
composio **
-
anon **
-
langchain toolkits. Support for Github, Gitlab, Gmail, Jira, MultiOn, Slack toolkits is planned.
** Since composio and anon are third-party providers, their tools require setting up account linking.
integration
tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user'smetadata
fields. -
-
Webhooks &
api_call
s (upcoming)
julep can build natural-language tools from openapi specs. Under the hood, we use langchain's NLA toolkit for this. Same asintegration
s, additional runtime parameters are loaded frommetadata
fields.
The Tool API provides the following endpoints for managing tools associated with an agent:
- List Tools (paginated):
GET /agents/{id}/tools
- Create a new Tool:
POST /agents/{id}/tools
- Update an existing Tool (overwrite):
PUT /agents/{id}/tools/{child_id}
- Update an existing Tool (merge):
PATCH /agents/{id}/tools/{child_id}
- Delete Tool:
DELETE /agents/{id}/tools/{child_id}
Here are some curl command examples for the main Tool endpoints:
- List Tools (paginated):
curl -X GET "https://api.julep.ai/api/agents/{agent_id}/tools?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a new Tool:
curl -X POST "https://api.julep.ai/api/agents/{agent_id}/tools" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "web_search",
"type": "function",
"function": {
"description": "Search the web for information",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
}
},
"required": ["query"]
}
}
}'
- Update an existing Tool (PUT):
curl -X PUT "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "updated_web_search",
"type": "function",
"function": {
"description": "Updated web search function",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query"
},
"limit": {
"type": "integer",
"description": "Number of results to return"
}
},
"required": ["query"]
}
}
}'
- Update an existing Tool (PATCH):
curl -X PATCH "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"function": {
"description": "Updated web search function description"
}
}'
- Delete Tool:
curl -X DELETE "https://api.julep.ai/api/agents/{agent_id}/tools/{tool_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
Remember to replace YOUR_API_KEY
, {agent_id}
, and {tool_id}
with your actual Julep API key, agent ID, and tool ID respectively.
Often, it's necessary to partial some arguments of a particular tool. You can do that by setting the x-integration-args
field on the metadata
of the required scope. For instance, say you have the following user-defined function tool:
name: check_account_status
description: Get the account status for a customer
parameters:
type: object
properties:
customer_id:
type: string
required: true
When chatting with a particular user, the customer_id
field is expected to be fixed. In this case, you can set it on the User
using:
{
"metadata": {
...
"x-integration-args": {
"function:check_account_status": {
"customer_id": 42
}
}
}
}
The convention for naming the fields for that object is "<tool-type>:<tool-name>"
. The values are partial-applied to the tool before being sent to the model.
This follows the precedence order of metadata
fields. For example, say you are interacting with the following session:
user:
id: 11
metadata:
x-integration-args:
favorite: Emma Roberts
agent:
id: 22
metadata:
x-integration-args:
favorite: Emma Watson
tools:
- type: function
name: send_fan_mail
parameters:
# ... favorite: ...
session:
id: 123
metadata:
x-integration-args:
favorite: Emma Stone
Then, the send_fan_mail
will be called with the value of favorite
set to the session's metadata
(as dictated by the precedence order) to "Emma Stone"
.
Doc
s are collection of text snippets (image support planned) that are indexed into a built-in vector database:
- They can be scoped to an agent or a user.
- Snippets are recalled inside sessions on the fly.
- The retrieval pipeline is optimized for general-purpose use cases.
- We use vector embedding models that strike a balance between accuracy and performance.
- Any snippets retrieved during a session are returned as part of the response for attribution.
- The embeddings are kept up to date as new models and techniques emerge.
- For advanced use cases, it might be necessary to roll your own. The pros of using julep are speed and automatic updates.
You can use the Doc
s by:
- Searching using a query or embedding directly, or
- When they are recalled within
Session
s based on the context.
We use the latest state-of-the-art open-source embedding model for producing the vector embeddings. As new models and techniques emerge, we migrate the existing Doc
s in the system to use them.
The Doc API provides the following endpoints:
- List Docs owned by a User (paginated):
GET /users/{id}/docs
- Create a Doc for a User:
POST /users/{id}/docs
- Delete a Doc for a User:
DELETE /users/{id}/docs/{child_id}
- List Docs owned by an Agent (paginated):
GET /agents/{id}/docs
- Create a Doc for an Agent:
POST /agents/{id}/docs
- Delete a Doc for an Agent:
DELETE /agents/{id}/docs/{child_id}
- Get Doc by id:
GET /docs/{id}
- Search Docs owned by a User:
POST /users/{id}/search
- Search Docs owned by an Agent:
POST /agents/{id}/search
- Embed a query for search:
POST /embed
Here are some curl command examples for the main Doc endpoints:
- List Docs owned by a User (paginated):
curl -X GET "https://api.julep.ai/api/users/{user_id}/docs?limit=10&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
- Create a Doc for a User:
curl -X POST "https://api.julep.ai/api/users/{user_id}/docs" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "My New Document",
"content": "This is the content of my new document."
}'
- Get Doc by id:
curl -X GET "https://api.julep.ai/api/docs/{doc_id}" \
-H "Authorization: Bearer YOUR_API_KEY"
- Search Docs owned by an Agent:
curl -X POST "https://api.julep.ai/api/agents/{agent_id}/search" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "search query",
"limit": 10,
"lang": "en-US"
}'
- Embed a query for search:
curl -X POST "https://api.julep.ai/api/embed" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"text": "text to embed"
}'
Remember to replace YOUR_API_KEY
with your actual Julep API key, and {user_id}
, {agent_id}
, and {doc_id}
with the specific IDs you're working with.
Task
s in Julep are Github Actions style workflows that define long-running, multi-step actions. You can use them to conduct complex actions by defining them step-by-step. They have access to all Julep integrations.
See loom video explaining this with an example.
A Task
is a workflow owned by an Agent
. It consists of the following fields:
Field | Type | Description |
---|---|---|
name |
string |
The name of the task (required for creation). |
description |
string |
A description of the task (default: ""). |
main |
WorkflowStep[] |
The main workflow steps (required, minimum 1 item). |
input_schema |
`Record | null` |
tools |
TaskTool[] |
Additional tools specific to this task (default: empty array). |
inherit_tools |
boolean |
Whether to inherit tools from the parent agent (default: true). |
Additionally, a Task
includes:
- An
id
(UUID, read-only) - Timestamps (
created_at
andupdated_at
, read-only) - Metadata (key-value pairs)
- Additional named workflows (as
Record<WorkflowStep[]>
)
A TaskTool
extends the CreateToolRequest
and includes:
Field | Type | Description |
---|---|---|
inherited |
boolean |
Whether the tool was inherited (read-only, default: false) |
There can be multiple named workflows in a task. main
is the entry point workflow for the task execution. Let's see an example of a task:
# An example Task definition
name: Daily Motivation
description: Provides daily motivation based on user preferences
input_schema:
type: object
properties:
about_user:
type: string
topics:
type: array
items:
type: string
user_email:
type: string
format: email
required: ["about_user", "topics", "user_email"]
tools:
- function:
name: send_email
description: Sends an email to the user
parameters:
type: object
properties:
subject:
type: string
content:
type: string
recipient:
type: string
required: ["subject", "content", "recipient"]
main:
# Pick a random topic.
# `evaluate` step takes a key-value object where the values are valid python *expressions*.
- evaluate:
chosen_topic: _["topics"][randint(len(_["topics"]))]
# Think about what support the user might need.
# Note: `inputs` and `outputs` are globals.
- prompt: You are a motivational coach and you are coaching someone who is {{inputs[0]["about_user"]}}. Think of the challenges they might be facing on the {{_["chosen_topic"]}} topic and what to do about them. Write down your answer as a bulleted list.
# Write a poem about it.
# Note: `_` stands for `outputs[-1]` i.e. the last step's output
- prompt: Write a short motivational poem about {{_["choices"][0].content}}
# Manually call the send_email function.
# `arguments` is an object where values are python expressions.
- tool:
name: send_email
arguments:
subject: '"Daily Motivation"'
content: _["choices"][0].content
# Sleep for a day
- sleep: 24*3600
# Start all over again
- workflow: main
arguments: inputs[0]
This example demonstrates a daily motivation task that:
- Selects a random topic and gets the current date.
- Generates motivational content based on the user's profile and chosen topic.
- Creates a motivational poem based on the generated content.
- Sends an email to the user with the motivational content.
- Sleeps for 24 hours before restarting the process.
Step Type | Description |
---|---|
Tool Call | Runs a specified tool with given arguments. |
Prompt | Runs a prompt using a model. You can override settings and interpolate variables using jinja templates. |
Evaluate | Accepts an object with values that are valid python expressions. The step runs the expressions and the result becomes the output of this step. |
Wait for input | Suspends the execution and waits for the caller to resume execution with an input. |
Log | Logs information during the workflow execution. |
Embed | Embeds text for semantic operations. |
Search | Searches for documents in the agent's doc store. |
Set | Sets a value in the workflow's key-value store. |
Get | Retrieves a value from the workflow's key-value store. |
Foreach | Runs a step for every value from a list in serial order. |
Map-reduce | Runs a step for every value of the input list in parallel. Requires a reduce expression to collect the results. |
Parallel | Executes multiple steps in parallel. |
Switch | Executes different steps based on a condition, similar to a switch statement in programming. |
If-else | Conditional step where the if field expression is evaluated. If the output is truthy then the then branch is executed, otherwise else branch. |
Sleep | Pauses the workflow execution for a specified number of seconds. |
Return | Ends the current workflow and optionally returns a value. |
Yield | Switches to another named workflow. Can add custom inputs (Default: output of previous steps). |
Error | Throws an error with the provided message and exits the workflow. |
Runs a specified tool with given arguments.
tool: send_email
arguments:
subject: "f'Daily Motivation: {_[\"chosen_topic\"]}'"
content: "_['choices'][0].content"
recipient: "inputs['user_email']"
In this example:
tool
is a string reference to the tool to be called.arguments
is an object where each value is a Python expression (TypedExpression). These expressions are evaluated at runtime.
Runs a prompt using a model. You can override settings and interpolate variables using jinja templates.
prompt: "You are a motivational coach. Write a short inspirational message about {{inputs['chosen_topic']}} for someone who is {{inputs['about_user']}}."
settings:
temperature: 0.7
max_tokens: 150
This example shows:
- A
prompt
using Jinja template syntax for variable interpolation. settings
to override default chat parameters for this specific prompt.
Accepts an object with values that are valid Python expressions. The step runs the expressions and the result becomes the output of this step.
evaluate:
chosen_topic: "random.choice(inputs['topics'])"
current_date: "datetime.now().strftime('%Y-%m-%d')"
message_count: "len(_['choices'])"
Each value in the evaluate
object is a Python expression (TypedExpression) that gets evaluated.
Suspends the execution and waits for the caller to resume execution with an input.
wait_for_input:
info:
message: "'Please provide feedback on the motivation message.'"
current_topic: "_['chosen_topic']"
timestamp: "datetime.now().isoformat()"
The info
object contains expressions that will be evaluated and provided to the user interface.
Logs information during the workflow execution.
log: "Generated motivation for topic: {{_['chosen_topic']}}"
The log
field uses a Jinja template for variable interpolation.
Embeds text for semantic operations.
embed:
text: "_['choices'][0].content"
model: "text-embedding-ada-002"
This step embeds the content generated in a previous step.
Searches for documents in the agent's doc store.
search:
query: "_['chosen_topic']"
limit: 5
This step searches for documents related to the chosen topic.
Sets a value in the workflow's key-value store.
set:
last_topic: "_['chosen_topic']"
motivation_count: "get('motivation_count', 0) + 1"
This step sets or updates values in the workflow's key-value store.
Retrieves a value from the workflow's key-value store.
get: last_topic
This step retrieves the value of "last_topic" from the key-value store.
Runs a step for every value from a list in serial order.
foreach:
in: "inputs['topics']"
do:
prompt: "Write a short tip about {{_}} for someone who is {{inputs['about_user']}}."
This step iterates over the input topics, generating a tip for each one.
Runs a step for every value of the input list in parallel. Requires a reduce expression to collect the results.
over: "inputs['topics']"
map:
prompt: "Generate a motivational quote about {{_}}."
reduce: "'\n'.join([f'{topic}: {quote}' for topic, quote in zip(inputs['topics'], results)])"
initial: "''"
parallelism: 5
This step generates quotes for multiple topics in parallel and then combines them into a single string.
Executes multiple steps in parallel.
parallel:
- prompt: "Generate a motivational quote about {{inputs['chosen_topic']}}."
- tool: get_weather
arguments:
location: "inputs['user_location']"
This step runs a prompt and a tool call in parallel.
Executes different steps based on a condition, similar to a switch statement in programming.
switch:
- case: "len(_['choices']) > 3"
then:
prompt: "Summarize the main points from the previous responses."
- case: "len(_['choices']) > 0"
then:
prompt: "Elaborate on the previous response."
- case: "_"
then:
prompt: "Start a new conversation about {{inputs['chosen_topic']}}."
This step chooses different actions based on the number of previous responses.
Conditional step where the if
field expression is evaluated. If the output is truthy then the then
branch is executed, otherwise else
branch.
if: "inputs['user_mood'] == 'positive'"
then:
prompt: "Great! Let's build on that positive energy. {{inputs['chosen_topic']}}"
else:
prompt: "I understand you're feeling down. Let's work on improving your mood through {{inputs['chosen_topic']}}."
This step chooses different prompts based on the user's mood.
Pauses the workflow execution for a specified number of seconds.
sleep:
seconds: uint16
minutes: uint16
hours: uint16
days: uint16
This step pauses the workflow for 24 hours.
Ends the current workflow and optionally returns a value.
return:
motivation_message: "_['choices'][0].content"
generated_at: "datetime.now().isoformat()"
This step ends the workflow and returns the generated motivation message along with a timestamp.
Switches to another named workflow. Can add custom inputs (Default: output of previous steps).
workflow: generate_weekly_summary
arguments:
recent_topics: "get('recent_topics', [])"
user_profile: "inputs['user_profile']"
This step switches to a "generate_weekly_summary" workflow, passing in recent topics and the user profile.
Throws an error with the provided message and exits the workflow.
error: "Failed to generate motivation: No suitable topic found"
This step throws an error and terminates the workflow execution.
These expanded examples provide a more comprehensive look at how each task step can be used in a Julep workflow, showcasing the flexibility and power of the task system.
An Execution
is an instance of a Task
that has been started with some input
.
At any given moment, it can be in one of these states:
Status | Description |
---|---|
"queued" | The execution is queued and waiting to start. |
"starting" | The execution is starting. |
"running" | The execution is running. |
"awaiting_input" | The execution is suspended and awaiting input to resume. |
"succeeded" | The execution has succeeded. |
"failed" | The execution failed for some reason. |
"cancelled" | The execution has been cancelled by the user. |
The state transitions of an Execution can be visualized as follows:
Mermaid Diagram
---
title: Execution state machine
---
stateDiagram-v2
[*] --> queued
queued --> starting
queued --> cancelled
starting --> cancelled
starting --> failed
starting --> running
running --> running
running --> awaiting_input
running --> cancelled
running --> failed
running --> succeeded
awaiting_input --> running
awaiting_input --> cancelled
cancelled --> [*]
succeeded --> [*]
failed --> [*]
ASCII Diagram
+----------+
| queued |
+----+-----+
|
+------+---------+
| |
v v
+----------+ +-----------+
+-----| starting | | cancelled |
| +----+-----+ +-----------+
| | ^
| +-----+-------+ |
| | | |
| v v |
| +-------+ +---------+ |
| | failed| | waiting |---+
| +-------+ +----+----+
| | ^
| | |
| v |
| +-------------+
+---------->| running |
+-------------+
| |
| |
+----v---v----+
| succeeded |
+-------------+
The Execution API provides the following endpoints:
- Get an Execution by id:
GET /executions/{id}
- Update an existing Execution:
PUT /executions/{id}
- Resume an execution with a task token:
POST /executions/resume
- Create an execution for a given task:
POST /tasks/{task_id}/executions
- List executions of a given task:
GET /tasks/{task_id}/executions
- List the Transitions of an Execution by id:
GET /executions/{id}/transitions
- Stream events emitted by a given execution:
GET /executions/{id}/transitions/stream
The main models related to Executions are:
Execution
: Represents the state and details of a task execution.CreateExecutionRequest
: Payload for creating a new execution.UpdateExecutionRequest
: Base model for updating an execution's status.StopExecutionRequest
: Request to stop an execution.ResumeExecutionRequest
: Request to resume an execution.TaskTokenResumeExecutionRequest
: Request to resume an execution using a task token.Transition
: Represents a state transition in the execution.TransitionEvent
: Represents an event emitted during a transition.
Transitions can be of various types, including "init", "finish", "wait", "resume", "error", "step", and "cancelled".
Executions in Julep follow a specific state transition model. The transitions are governed by both the execution status and the transition type. Here's a detailed breakdown:
The valid transitions between execution statuses are as follows:
queued
→starting
starting
→running
,awaiting_input
,cancelled
,succeeded
,failed
running
→running
,awaiting_input
,cancelled
,succeeded
,failed
awaiting_input
→running
,cancelled
cancelled
,succeeded
,failed
→ (terminal states, no further transitions)
Executions can go through various transition types, each representing a specific stage or action in the execution process:
init
: Initializes the executioninit_branch
: Starts a new branch in the executionfinish
: Completes the execution successfullyfinish_branch
: Completes a branch in the executionwait
: Pauses the execution, waiting for inputresume
: Resumes a paused executionerror
: Indicates an error occurredstep
: Represents a step in the execution processcancelled
: Indicates the execution was cancelled
The execution follows specific rules for transition type sequences:
init
can be followed bywait
,error
,step
,cancelled
,init_branch
, orfinish
init_branch
can be followed bywait
,error
,step
,cancelled
, orfinish_branch
wait
can only be followed byresume
orcancelled
resume
,step
, andfinish_branch
can be followed bywait
,error
,cancelled
,step
,finish
,finish_branch
, orinit_branch
finish
,error
, andcancelled
are terminal states with no further transitions
Each transition type corresponds to a specific execution status:
init
→starting
init_branch
,resume
,step
,finish_branch
→running
wait
→awaiting_input
finish
→succeeded
error
→failed
cancelled
→cancelled
This state transition model ensures that executions follow a consistent and predictable flow, allowing for complex workflows while maintaining clear status tracking.