# FAQ
Source: https://docs.julep.ai/FAQ
Frequently Asked Questions about Julep Platform
# Julep Platform FAQ
This comprehensive FAQ document covers all aspects of the Julep platform, from architecture to troubleshooting. The information is organized by category for easy navigation.
## Table of Contents
1. [Architecture & System Design](#architecture--system-design)
2. [Data Model & Storage](#data-model--storage)
3. [Task Execution & Workflow](#task-execution--workflow)
4. [Agents API](#agents-api)
5. [Worker System & Integration](#worker-system--integration)
6. [Development & Deployment](#development--deployment)
7. [Performance & Optimization](#performance--optimization)
8. [Security & Compliance](#security--compliance)
9. [Advanced Use Cases & Patterns](#advanced-use-cases--patterns)
10. [Troubleshooting & Common Issues](#troubleshooting--common-issues)
***
## Architecture & System Design
### Q: What is the overall system architecture of Julep, including all core components and their interactions?
Julep is a distributed system built on a microservices architecture designed to orchestrate complex AI workflows. The main components include:
* **Client Applications**: Initiate requests to the Julep system
* **Gateway**: Entry point for all API requests, handles authentication and load balancing (implemented using Traefik)
* **Agents API**: Provides REST endpoints for managing agents, tasks, sessions, and documents; initiates workflows in Temporal
* **Temporal Workflow Engine**: Orchestrates durable workflow execution, retries, and state management
* **Worker System**: Executes workflows and activities defined in Temporal by polling for tasks
* **LiteLLM Proxy**: Provides a unified interface for interacting with various LLM providers
* **Memory Store**: Provides persistent storage using PostgreSQL/TimescaleDB for relational data and vector embeddings
* **Integration Service**: Enables connections with external tools and APIs
### Q: How does Julep handle distributed task execution and what role does Temporal play in the architecture?
Julep handles distributed task execution primarily through the Temporal Workflow Engine:
* **Workflow Orchestration**: Temporal ensures durable execution of workflows, handling retries and maintaining state across failures
* **Task Queues**: The Agents API initiates workflows by sending requests to Temporal, which places them on task queues like `julep-task-queue`
* **Worker Execution**: Workers poll Temporal for tasks and execute activities like LLM calls, tool operations, and data interactions
* **State Management**: Temporal persists workflow execution state in PostgreSQL, ensuring long-running processes can recover from failures
### Q: What are the key design decisions behind separating agents-api, memory-store, integrations-service, and other components?
The separation follows microservices principles:
* **Modularity and Independent Scaling**: Each service can be developed, deployed, and scaled independently
* **Separation of Concerns**: Each component handles specific functionalities:
* `agents-api`: Manages agent definitions, tasks, sessions, and orchestrates workflows
* `memory-store`: Handles all data persistence including relational data and vector embeddings
* `integrations-service`: Provides standardized interface for external tool usage
* `llm-proxy`: Centralizes LLM interactions with a unified API
* **Resilience**: Failures in one service are isolated from others
* **Technology Flexibility**: Different services can use different technologies if needed
### Q: How does the gateway component route requests between different services?
The Gateway component uses Traefik and routes requests based on defined rules:
* Requests to `/api/*` are routed to the Agents API service
* Requests to `/tasks-ui/*` go to the Temporal UI service
* `/v1/graphql` requests are directed to the Hasura service
* In multi-tenant setups, it enforces JWT-based authentication and forwards `X-Developer-Id` headers for resource isolation
### Q: What is the role of the blob-store and how does it integrate with S3-compatible storage?
The blob-store is used for persistent storage of large data, specifically for Temporal workflow data when `USE_BLOB_STORE_FOR_TEMPORAL` is enabled. It integrates with S3-compatible storage through environment variables:
* `S3_ENDPOINT`, `S3_ACCESS_KEY`, and `S3_SECRET_KEY` for connection
* `BLOB_STORE_BUCKET` defines the bucket name
* `BLOB_STORE_CUTOFF_KB` sets the size threshold for blob storage
### Q: How does the llm-proxy (LiteLLM) handle different language model providers?
LiteLLM provides a unified interface to multiple LLM providers:
* Supports providers like OpenAI, Anthropic, Gemini, Groq, and OpenRouter
* Configuration defined in `litellm-config.yaml` with model names, parameters, and API keys
* Handles response patching for consistency (e.g., changing `finish_reason` from "eos" to "stop")
* Tracks token usage and costs in PostgreSQL
* Implements request caching using Redis and supports parallel forwarding
### Q: What are the scalability patterns and limitations of the current architecture?
Julep's architecture supports scalability through:
* **Agents API**: Horizontal scaling via multiple instances, configurable with `GUNICORN_WORKERS`
* **Worker**: Multiple workers with concurrency control using `TEMPORAL_MAX_CONCURRENT_ACTIVITIES`
* **Memory Store**: PostgreSQL connection pooling with `POOL_MAX_SIZE`
* **LiteLLM**: Request caching and parallel forwarding
* **Temporal**: Durable workflow execution that scales to handle concurrent tasks
### Q: How does Julep ensure high availability and fault tolerance across services?
High availability is achieved through:
* Temporal's durable execution model with automatic retries
* Microservices architecture allowing independent service failures
* PostgreSQL for persistent state storage
* Worker pools for distributed task execution
* Connection pooling and retry mechanisms
***
## Data Model & Storage
### Q: What is the complete data model including relationships between Agents, Tasks, Tools, Sessions, Entries, and Executions?
The core entities and their relationships:
* **Developer**: Manages Agents, Users, and owns Tasks
* **Agent**: Has Tasks, defines Tools, owns Docs, participates in Sessions
* **User**: Owns Docs and participates in Sessions
* **Task**: Contains WorkflowSteps and is executed as Executions
* **Execution**: Logs Transitions and tracks task execution state
* **Session**: Contains Entries (conversation history)
* **Entry**: Individual messages within a Session
* **Tool**: Capabilities available to Agents
* **Doc**: Documents with embeddings for knowledge base
All entities use UUIDs for identification and include `developer_id` for multi-tenancy.
### Q: How does the memory-store handle vector embeddings and similarity search?
The memory store provides vectorized document storage:
* Documents have an `embeddings` field stored in `docs_embeddings_store` table
* Supports three search types:
* **Vector Search**: `search_docs_by_embedding` for semantic similarity
* **Text Search**: `search_docs_by_text` using PostgreSQL full-text search
* **Hybrid Search**: `search_docs_hybrid` combining both approaches
* Uses cosine similarity for vector comparisons
* Implements Maximum Marginal Relevance (MMR) for result diversity
### Q: What PostgreSQL and TimescaleDB features are leveraged for time-series data?
While PostgreSQL is the primary database, specific TimescaleDB features are not explicitly detailed in the codebase. The system uses:
* Standard PostgreSQL timestamps (`created_at`, `updated_at`) for temporal data
* Time-based filtering in queries (e.g., `list_entries` with date ranges)
* No explicit TimescaleDB-specific features documented
### Q: How are agent instructions and task definitions stored and versioned?
* **Agent Instructions**: Stored as `string` or `array[string]` in the Agent entity
* **Task Definitions**: Stored as Task entities with fields like `name`, `description`, `input_schema`, `main` (workflow steps), and `tools`
* **Versioning**: Handled through UUID changes - altering a UUID creates a new entity while preserving the original
* Timestamps (`created_at`, `updated_at`) provide implicit version tracking
### Q: What is the schema for storing conversation history and context?
Conversation history uses two main entities:
* **Session**: Contains `id`, `user`, `agent`, `situation` (context), `system_template`, and `metadata`
* **Entry**: Contains `id`, `session_id`, `role` (user/assistant/system), `content` (string or JSON), `source`, and `timestamp`
* Sessions group entries and maintain context across conversations
### Q: How does Julep handle data partitioning and archiving for long-running agents?
The codebase does not contain explicit information about data partitioning or archiving strategies. The system uses:
* Multi-tenancy through `developer_id` filtering
* Pagination support for large datasets
* Time-based filtering capabilities
* No documented automatic archiving policies
### Q: What are the indexing strategies for optimizing query performance?
Indexing strategies include:
* Trigram indexes for text search (indicated by `trigram_similarity_threshold` parameter)
* Vector indexes for embedding similarity searches
* Document chunking and embedding storage for efficient retrieval
* Use of prepared statements for query optimization
***
## Task Execution & Workflow
### Q: How does the TaskExecutionWorkflow handle complex multi-step operations?
The TaskExecutionWorkflow orchestrates multi-step operations by:
* Processing different WorkflowStep types through dedicated handlers
* Managing state transitions between steps
* Integrating with Temporal for durability and reliability
* Using `handle_step` method to process each step type
* Evaluating expressions within steps using `eval_step_exprs`
### Q: What are all the possible workflow step types and their configurations?
**Basic Steps:**
* **PromptStep**: Sends prompts to LLMs and handles responses
* **ToolCallStep**: Executes tool calls (functions, integrations, APIs, system operations)
* **EvaluateStep**: Evaluates expressions and returns results
* **SetStep**: Sets values in execution state
* **GetStep**: Retrieves values from execution state
* **LogStep**: Logs messages during execution
* **ReturnStep**: Returns a value and completes workflow
* **ErrorWorkflowStep**: Raises an error and fails the workflow
* **SleepStep**: Pauses execution for specified duration
* **WaitForInputStep**: Pauses for external input
**Control Flow Steps:**
* **IfElseWorkflowStep**: Conditional branching based on condition
* **SwitchStep**: Multi-way branching based on case evaluation
* **ForeachStep**: Iterates over collections
* **MapReduceStep**: Maps function over items with optional parallelism
* **YieldStep**: Yields execution to another workflow
* **ParallelStep**: Executes steps in parallel (not yet implemented)
### Q: How does the state machine handle transitions between different execution states?
The state machine tracks execution through:
* **States**: `queued`, `starting`, `running`, `succeeded`, `failed`, `cancelled`
* **Transitions**: Record state changes with types: `init`, `step`, `finish`, `error`, `cancelled`
* Each transition includes `output`, `current`, and `next` workflow steps
* Transitions stored in `execution_transitions` table
* `create_execution_transition` function records all state changes
### Q: What happens when a task fails midway through execution?
When a task fails:
* Execution status transitions to "failed"
* An "error" transition is created with the error message
* The `error` field of the Execution object is populated
* The system tracks the failure point and error details
* Workflow implements retry policies for retryable errors
* Non-retryable errors cause immediate failure
### Q: How are conditional branches and loops implemented in workflows?
**Conditional Branches:**
* `IfElseWorkflowStep`: Evaluates condition and executes "then" or "else" branch
* `SwitchStep`: Multi-way branching with multiple cases
**Loops:**
* `ForeachStep`: Iterates over collections processing each item
* `MapReduceStep`: Maps functions over collections with optional parallel execution
These are processed by dedicated handlers like `_handle_IfElseWorkflowStep` and `_handle_ForeachStep`.
### Q: What is the retry and error handling strategy for failed steps?
* **Error Classification**: Errors classified as retryable or non-retryable
* **Retry Policy**: `DEFAULT_RETRY_POLICY` applied to retryable errors
* **Max Retries**: Workflow fails if max retries exceeded
* **Error Transitions**: "error" transitions record failure states
* **Last Error Tracking**: `last_error` attribute stores recent errors
### Q: How does Julep handle long-running tasks and prevent timeouts?
Julep uses Temporal's timeout mechanisms:
* `schedule_to_close_timeout` and `heartbeat_timeout` for activities
* Activity and workflow heartbeats ensure progress tracking
* Large data handling via `RemoteObject` pattern to optimize memory
* Blob storage for data exceeding size thresholds
### Q: What are the mechanisms for task cancellation and cleanup?
* **Workflow Cancellation**: Handled through Temporal's cancellation features
* **State Management**: Execution can transition to "cancelled" state
* **Persistence**: All transitions persisted for state restoration
* **Cleanup**: Status updates and transition history provide cleanup context
***
## Agents API
### Q: What are all the endpoints available in the Agents API and their use cases?
The Agents API provides these endpoints:
* `/agents`: Create, retrieve, update, delete agent definitions
* `/tasks`: Define and execute tasks, retrieve workflow definitions
* `/sessions`: Manage conversational sessions and conversation history
* `/executions`: Track task executions and monitor status
* `/docs`: Handle document storage, search, and retrieval with embeddings
* `/tools`: Define and manage agent tools
* `/users`: Manage user accounts and authentication
* `/responses`: OpenAI-compatible interface for LLM responses
### Q: How does session management work and what data is maintained per session?
Session management maintains conversation state:
* **Session Data**: `id`, `agent_id`, `user_id`, `created_at`, situation context
* **Entries**: Individual conversation turns with role, content, and timestamps
* Sessions created with `julep.sessions.create` linking agent and user
* Messages added via `julep.sessions.chat` with role and content
* Pagination support for retrieving conversation history
### Q: What are the different types of tools an agent can use and how are they configured?
Tool types available:
* **Web Search Tool**: Performs web searches with domain filtering
* **Function Tools**: OpenAI-compatible function calling format
* **System Tools**: Internal Julep resources (e.g., `create_julep_session`, `session_chat`)
* **Integration Tools**: External services (e.g., BrowserBase, email providers)
Configuration includes name, type, description, and type-specific parameters.
### Q: How does document storage and retrieval work for agent knowledge bases?
Documents (`Doc` entities) provide agent knowledge:
* Stored in PostgreSQL with embeddings in `docs_embeddings_store`
* Owned by either Agent or User
* Three search methods: text-based, embedding-based, hybrid
* Document operations: create, retrieve, list, search
* Supports metadata filtering and pagination
### Q: What are the authentication and authorization mechanisms for the API?
Two authentication modes:
**Single-Tenant Mode:**
* Uses `AGENTS_API_KEY` for authentication
* Default developer ID assumed
* `X-Auth-Key` header required
**Multi-Tenant Mode:**
* JWT-based authentication via Gateway
* `X-Developer-Id` header for resource isolation
* Developer-specific data access control
* JWT must contain sub, email, exp, iat claims
### Q: How are agent instructions processed and validated?
* Instructions stored as string or array of strings
* Validated by Pydantic models from OpenAPI schemas
* Included in agent's `default_system_template`
* Dynamic rendering supports both single and array formats
* TypeSpec definitions ensure consistent structure
### Q: What are the rate limiting and quota management strategies?
Rate limiting and quota management are planned features:
* `max_free_sessions` and `max_free_executions` environment variables defined
* Implementation details not yet available
* Listed as future enhancement in roadmap
### Q: How does the API handle streaming responses for real-time interactions?
Streaming support is currently planned but not implemented:
* Open Responses API designed for OpenAI compatibility
* `stream` configuration option available
* Full streaming implementation pending
***
## Worker System & Integration
### Q: How does the worker system integrate with different LLM providers?
The Worker integrates with LLMs through LiteLLM Proxy:
* LiteLLM acts as unified interface to various providers
* Supports OpenAI, Anthropic, Gemini, Groq, OpenRouter
* Worker makes LLM calls via LiteLLM Proxy
* Configuration in `litellm-config.yaml`
* Handles authentication, routing, and caching
### Q: What are the system activities available and how do they work?
System activities include:
* **LLM Calls**: Through LiteLLM Proxy
* **Tool Operations**: Via Integration Service
* **Data Operations**: Reading/writing to Memory Store
* **PG Query Step**: Direct PostgreSQL queries
* Activities invoked with `StepContext` and appropriate definitions
### Q: How does Julep handle tool execution and external API calls?
Tool execution handled by Integration Service:
* **Integration Tools**: Connect to external services with provider/method specs
* **System Tools**: Operate on internal Julep resources
* `ToolCallStep` defines tool and arguments
* Worker invokes Integration Service for execution
* Examples: email sending, browser automation, document search
### Q: What is the sandboxing mechanism for Python expression evaluation?
Python expression sandboxing:
* `validate_py_expression` function validates expressions
* Identifies expressions starting with `$`, `_`, or containing `{{`
* Checks for syntax errors, undefined names, unsafe operations
* Limited scope with allowed names: `_`, `inputs`, `outputs`, `state`, `steps`
* Prevents dunder attribute access and unapproved function calls
### Q: How are integration credentials managed and secured?
Integration credentials managed through:
* Credentials stored in `setup` parameters of tool definitions
* API keys provided in task definition YAML
* Environment variables for service-level credentials
* No explicit encryption details documented
### Q: What are the patterns for building custom integrations?
Custom integrations defined as Tool entities:
* Type set to `integration`
* Specify `provider`, `method`, and `setup` parameters
* Include provider-specific configuration (API keys, endpoints)
* Used within Task workflows as ToolCallStep
* Examples: Browserbase, email providers, Cloudinary
### Q: How does the browser automation integration work?
Browser automation workflow:
1. Create Julep session for AI agent
2. Create browser session using Browserbase
3. Store session info (browser\_session\_id, connect\_url)
4. Perform actions via `perform_browser_action` tool
5. Interactive loop with agent planning and execution
6. Screenshot capture for visual feedback
### Q: What are the performance optimizations for worker pools?
Worker performance optimizations:
* `TEMPORAL_MAX_CONCURRENT_ACTIVITIES` controls concurrency
* `TEMPORAL_MAX_ACTIVITIES_PER_SECOND` limits activity rate
* `GUNICORN_WORKERS` for integration service scaling
* Connection pooling and timeout configurations
* LiteLLM caching and parallel forwarding
***
## Development & Deployment
### Q: What is the recommended development workflow for building with Julep?
Development workflow uses Docker Compose with watch mode:
* Changes in source directories trigger automatic sync/restart
* `agents-api`: Watches `./agents_api` and `gunicorn_conf.py`
* `worker`: Watches `./agents_api` and `Dockerfile.worker`
* `integrations`: Watches its directory for changes
* Lock file or Dockerfile changes trigger rebuilds
### Q: How should developers set up their local environment for testing?
Local setup primarily uses Docker:
1. Create project directory: `mkdir julep-responses-api`
2. Download and edit `.env` file
3. Download Docker Compose file
4. Run containers: `docker compose up --watch`
5. Verify with `docker ps`
Alternative CLI installation available via `npx` or `uvx`.
### Q: What are the deployment options and best practices?
Deployment options:
**Single-Tenant Mode:**
* All users share context
* `SKIP_CHECK_DEVELOPER_HEADERS=True`
* Requires `AGENTS_API_KEY`
**Multi-Tenant Mode:**
* Isolated resources per developer
* `AGENTS_API_MULTI_TENANT_MODE: true`
* JWT validation via Gateway
**Best Practices:**
* Use environment variables for configuration
* Implement layered security (API keys, JWT tokens)
* Enable independent component scaling
* Configure appropriate connection pools
### Q: How does the TypeSpec code generation work and when to use it?
TypeSpec code generation:
* TypeSpec files define data models (e.g., `models.tsp`)
* `scripts/generate_openapi_code.sh` generates code
* Creates Pydantic models and OpenAPI schemas
* Edit TypeSpec files, not generated code
* Regenerate after model changes
### Q: What are the testing strategies for agents and workflows?
Testing strategies include:
* Unit tests for entities (agents, docs, sessions, etc.)
* Integration tests simulating scenarios
* Workflow tests using `unittest.mock.patch`
* Test fixtures for consistent data
* Ward framework for test organization
### Q: How to debug failed task executions and trace through workflows?
Debugging approach:
* Check Execution status and error field
* List transitions to trace execution flow
* Examine transition outputs and types
* Use LogStep for workflow logging
* Error transitions indicate failure points
### Q: What are the monitoring and observability features?
Limited monitoring information available:
* Execution state and transition logging
* No explicit monitoring features documented
* Prometheus and Grafana mentioned in architecture
* Detailed observability features not specified
### Q: How to handle database migrations in production?
Database migration information not explicitly documented:
* PostgreSQL used as primary database
* Migration files exist in memory-store
* Production migration process not detailed
***
## Performance & Optimization
### Q: What are the performance characteristics of different operations?
Performance characteristics:
* MapReduceStep supports sequential or parallel execution
* API response times reduced by 15% (per changelog)
* Parallel processing improves collection operations
* Connection pooling optimizes database access
* Caching planned for future optimization
### Q: How does Julep handle concurrent agent executions?
Concurrent execution handled through:
* TaskExecutionWorkflow with Temporal orchestration
* Worker pools for distributed execution
* State isolation per execution
* Temporal manages workflow concurrency
* StepContext provides execution isolation
### Q: What are the caching strategies employed across the system?
Current caching:
* LiteLLM Proxy implements request caching
* Redis used for LiteLLM cache storage
* Web Search Tool has result caching
* Advanced caching mechanisms planned
### Q: How to optimize memory usage for large conversation histories?
Memory optimization strategies:
* Pagination with `limit` and `offset` parameters
* Maximum limit of 1000 entries per request
* `search_window` for time-based filtering (default 4 weeks)
* Token counting per entry
* RemoteObject pattern for large data
### Q: What are the bottlenecks in the current architecture?
Potential bottlenecks:
* Large data retrieval without proper pagination
* Complex database queries on large tables
* Multi-tenancy query filtering overhead
* Temporal workflow state management at scale
* JSON aggregation in history queries
### Q: How does connection pooling work for database access?
Connection pooling configuration:
* `connection_lifetime`: 600 seconds
* `idle_timeout`: 180 seconds
* `max_connections`: 50
* `retries`: 1
* `use_prepared_statements`: true
* `POOL_MAX_SIZE` configurable (default: CPU count, max 10)
### Q: What are the best practices for writing efficient task definitions?
Best practices:
* Define clear input schemas
* Use modular workflow steps
* Implement proper error handling
* Avoid infinite loops in recursive patterns
* Use appropriate tool types
* Monitor execution status and transitions
* Leverage parallel processing where applicable
***
## Security & Compliance
### Q: How does Julep handle sensitive data and ensure data privacy?
Data privacy ensured through:
* Multi-tenant architecture with resource isolation
* Developer-specific data access via `developer_id`
* `X-Developer-Id` header for request routing
* Separate data storage per developer
### Q: What are the security measures for multi-tenant deployments?
Multi-tenant security:
* JWT-based authentication at Gateway
* JWT validation with required claims (sub, email, exp, iat)
* `X-Developer-Id` header enforcement
* Developer ID verification against database
* Resource isolation by developer
### Q: How are API keys and secrets managed throughout the system?
Secret management:
* Environment variables for service credentials
* API keys in tool setup parameters
* `AGENTS_API_KEY` and `JWT_SHARED_KEY` for auth
* LLM provider keys as environment variables
* No explicit encryption details provided
### Q: What audit logging capabilities are available?
Audit logging:
* Currently limited implementation
* Listed as planned feature in roadmap
* Usage tracking for LLM calls (tokens and costs)
* Comprehensive audit logging pending
### Q: How does Julep ensure secure execution of user-provided code?
Secure code execution through:
* Task-based workflow system with YAML definitions
* Controlled tool invocation with explicit permissions
* Python expression validation
* Structured workflow steps
* No arbitrary code execution
### Q: What are the network security considerations for deployment?
Network security:
* API key authentication for single-tenant
* JWT tokens for multi-tenant
* Gateway-level authentication
* HTTPS/TLS support implied
* Service-to-service communication within network
***
## Advanced Use Cases & Patterns
### Q: What are examples of complex multi-agent workflows?
**Browser Use Assistant:**
* Session initialization
* Browser session creation
* Interactive agent-browser loop
* Screenshot feedback
* Goal-oriented task completion
**Email Assistant:**
* Email input processing
* Query generation
* Documentation search
* Response generation
* Automated email sending
**Video Processing:**
* Natural language instructions
* Cloudinary integration
* Transformation generation
* Video processing execution
### Q: How to implement human-in-the-loop patterns?
Human-in-the-loop not explicitly documented:
* `WaitForInputStep` provides pause mechanism
* Session-based interactions allow user input
* No dedicated approval workflow patterns
* Can be built using existing primitives
### Q: What are the patterns for building conversational agents with memory?
Conversational memory patterns:
* Session and Entry entities maintain history
* `previous_response_id` links responses
* Session metadata and custom templates
* Persistent conversation state
* Context maintained across interactions
### Q: How to implement custom tool integrations?
Custom tool implementation:
1. Define Tool entity with type `integration`
2. Specify provider, method, setup parameters
3. Use `@function_tool` decorator for functions
4. Add to agent's tool list
5. Invoke via ToolCallStep in workflows
### Q: What are the best practices for handling structured data extraction?
Structured data handling:
* Tools accept and return structured JSON
* Evaluate steps process tool outputs
* Response objects contain structured data
* Pydantic models ensure data validation
* TypeSpec defines consistent schemas
### Q: How to build agents that can learn and adapt over time?
Learning/adaptation features limited:
* Agent instructions can be updated
* Metadata field allows dynamic information
* No explicit learning mechanisms
* Adaptation through instruction updates
* Memory through conversation history
### Q: What are the patterns for building agents that can collaborate?
Agent collaboration not documented:
* Current model focuses on single agents
* No inter-agent communication patterns
* Sessions link one agent to users
* Collaboration would require custom implementation
***
## Troubleshooting & Common Issues
### Q: What are the most common errors and how to resolve them?
**Python Expression Errors:**
* Syntax errors: Fix malformed expressions
* Undefined names: Use allowed names only
* Unsafe operations: Avoid dunder attributes
* Runtime errors: Check for division by zero
* Unsupported features: Avoid lambdas, walrus operator
**Schema Validation Errors:**
* Pydantic validation failures
* Adjust JSON to match expected schema
**Integration Errors:**
* ApplicationError for missing tools
* Verify tool definitions and availability
### Q: How to debug issues with task execution?
Debugging steps:
1. Check Execution status field
2. Review error messages in Execution object
3. List and examine transitions
4. Check transition outputs and types
5. Use LogStep for additional logging
6. Trace workflow step progression
### Q: What are the common performance problems and solutions?
Performance issues and solutions:
* **Large collections**: Use MapReduceStep parallelism
* **Rate limits**: Implement SleepStep delays
* **Memory usage**: Paginate large result sets
* **Database queries**: Ensure proper indexing
* **Concurrent execution**: Configure worker pools
### Q: How to handle edge cases in agent conversations?
Edge case handling:
* `IfElseWorkflowStep` for conditional logic
* `SwitchStep` for complex branching
* `WaitForInputStep` for user input
* `ErrorWorkflowStep` for invalid states
* Proper error handling in workflows
### Q: What are the known limitations and workarounds?
**Expression Limitations:**
* No set comprehensions, lambdas, walrus operator
* Use alternative Python constructs
**Backwards Compatibility:**
* Old expression formats supported
* Use `$` prefix for consistency
**ParallelStep Not Implemented:**
* Use MapReduceStep with parallelism instead
**Streaming Not Available:**
* Planned feature, not yet implemented
**Rate Limiting:**
* Basic environment variables defined
* Full implementation pending
***
## Additional Resources
* [System Architecture Wiki](https://deepwiki.com/wiki/julep-ai/julep#2)
* [Data Model Wiki](https://deepwiki.com/wiki/julep-ai/julep#2.2)
* [TaskExecutionWorkflow Wiki](https://deepwiki.com/wiki/julep-ai/julep#3)
* [Workflow Steps Wiki](https://deepwiki.com/wiki/julep-ai/julep#3.1)
This FAQ is generated from the Julep platform documentation and codebase analysis. For the most up-to-date information, please refer to the official Julep documentation and repository.
# Agentic Patterns
Source: https://docs.julep.ai/advanced/agentic-patterns
Learn about common patterns and best practices for building Julep agents
## Overview
This guide covers common patterns and best practices for building effective Julep agents, inspired by a blog written by Anthropic on [Building effective agents](https://www.anthropic.com/research/building-effective-agents).
## Core Workflow Patterns
### 1. Prompt Chaining
Break tasks into sequential steps, where each step’s output feeds the next. Can be used for:
* Content generation and subsequent checks
* Translation/localization in multiple stages
* Ensuring quality between discrete transformations
**Example implementation:**
```yaml theme={"dark"}
main:
# Step 1: Generate initial content
- prompt:
role: system
content: >-
$ f'''Generate marketing copy for product X based on the following:
target_audience: {_.audience}
product_features: {_.features}
keywords: {_.seo_keywords}'''
unwrap: true
# Step 2: Quality check gate
- evaluate:
quality_check: $ _.content
# Step 3: Translation
- prompt:
role: system
content: Translate the approved content to Spanish
unwrap: true
```
### 2. Routing Pattern
Act as a “traffic controller” by detecting request types and sending them to the correct handler. Ideal when inputs are diverse or require specialized expertise.
Steps generally include:
1. Classification of incoming data
2. Routing to handler modules
3. Specialized processing
4. (Optional) Aggregation of results
**Example implementation:**
```yaml [expandable] theme={"dark"}
main:
# Classification step
- prompt:
role: system
content: Classify the input query type
unwrap: true
# Route based on classification
- switch:
- case: $ _.classification == "technical_support"
then:
- tool: handle_technical
arguments:
...
- case: $ _.classification == "billing"
then:
- tool: handle_billing
arguments:
...
- case: $ _.classification == "general"
then:
- tool: handle_general
arguments:
...
```
### 3. Parallelization Pattern
Execute subtasks concurrently by either dividing the workload (sectioning) or collecting multiple perspectives (voting).
Keys to consider:
• Parallel processes for performance or redundancy
• Syncing results and handling errors
• Aggregating diverse outputs
**Example implementations:**
1. Sectioning:
```yaml [expandable] theme={"dark"}
tools:
- name: aggregate_results
type: ... # depends on the specific needs
# Custom workflow to run a subtask
run_subtask:
- ....
# Main workflow
main:
- prompt:
role: system
content: >
$ f'''Break this task into multiple subtasks.
Here is the task: {_.task}'''
unwrap: true
- over: $ _.subtasks
do:
- workflow: run_subtask
arguments:
...
- tool: aggregate_results
arguments:
results: $ _
```
2. Voting:
```yaml [expandable] theme={"dark"}
tools:
- name: perform_voting
description: Perform voting on the results of running the task instances, and return the majority best result.
type: ... # depends on the specific needs
# Custom workflow to run a subtask
run_subtask:
- ....
# Main workflow
main:
- over: $ _.main_tasks
do:
- workflow: run_subtask # Run the same task multiple times (given that the `run_subtask` workflow is non-deterministic)
arguments:
...
- tool: perform_voting
arguments:
results: $ _
- evaluate:
final_result: $ _
```
### 4. Orchestrator-Workers Pattern
Use a central “orchestrator” that delegates subtasks to multiple “worker” agents and integrates their outputs. Best for:
* Large or dynamic multi-step tasks
* Coordinating various specialized capabilities
* Flexible task distribution
**Example implementation:**
```yaml theme={"dark"}
main:
# Orchestrator planning
- prompt:
role: system
content: $ f'''Break down the task into subtasks. Here is the task: {_.task}'''
unwrap: true
# Worker delegation
- foreach:
in: $ _.subtasks
do:
tool: assign_worker
arguments:
task: $ _
```
### 5. Evaluator-Optimizer Pattern
Create iterative feedback loops to refine outputs until they meet preset criteria. Suitable for:
• Content refinement
• Code reviews
• Detailed document enhancements
General flow:
1. Generate an initial result
2. Evaluate against criteria
3. Provide improvement feedback
4. Optimize/retry until goals are met
**Example implementation:**
```yaml [expandable] theme={"dark"}
tools:
- name: score_content
description: Score the content based on the criteria. Returns a json object with a score between 0 and 1, and a feedback string.
type: function
function:
parameters:
type: object
properties:
content:
type: string
description: Content to score
# Subworkflow to evaluate content
evaluate_content:
- tool: score_content
arguments:
content: $ _.content
- if: $ _.score < 0.5 # If the content does not meet the criteria, improve it
then:
- workflow: improve_content
arguments:
content: $ steps[0].input.content # steps[0].input is the main input of this workflow
feedback: $ _.feedback # _ is the output of the score_content tool call
else:
evaluate:
final_content: $ steps[0].input.content
# Subworkflow to improve content
improve_content:
- prompt:
role: system
content: $ f'''Improve the content based on the feedback. Here is the feedback: {_.feedback}'''
unwrap: true
- workflow: evaluate_content
arguments:
content: $ _
main:
# Initial generation
- prompt:
role: system
content: $ f'''Generate initial content. Here is the task: {_.task}'''
unwrap: true
# Evaluation loop
- loop:
while: $ not _.meets_criteria
do:
- tool: evaluate_content
- tool: improve_content
```
**Explanation:**
1. The `evaluate_content` subworkflow:
* Takes content as input and scores it using a scoring tool
* If the score is below 0.5, it triggers the improvement workflow
* Uses a special variable (`_`) to manage content and feedback between workflows
* Returns the final content once quality criteria are met
2. The `improve_content` subworkflow:
* Receives content and feedback from the evaluation
* Uses an LLM to improve the content based on specific feedback
* Automatically triggers another evaluation cycle by calling evaluate\_content
The main workflow ties these together by:
* Generating initial content from a task description
* Running a continuous loop that alternates between evaluation and improvement
* Only completing when content meets the defined quality criteria
This creates a powerful feedback loop where content is repeatedly refined based on specific feedback until it reaches the desired quality level. The pattern is particularly useful for tasks requiring high accuracy or quality, such as content generation, code review, or document analysis.
## Best Practices
- **1. Minimal Steps**: Use minimal steps and only add complexity when it clearly improves results.
- **1. Documentation**: Provide clear documentation, examples, and usage guidelines for each tool.
- **1. Guardrails**: Include feedback loops, define stopping conditions, and surface potential issues.
- **1. Validation**: Thoroughly test in controlled environments and measure against success criteria.
- **1. Checkpoints**: Establish checkpoints for approval, ensure transparency, and maintain easy-to-audit workflows.
## Conclusion
These patterns represent proven approaches from production implementations. Choose and adapt them based on your specific use case requirements and complexity needs.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Architecture Deep Dive
Source: https://docs.julep.ai/advanced/architecture-deep-dive
Understand the core architecture and components of Julep
## Overview
Think of Julep as a platform that combines both client-side and server-side components to help you build advanced AI agents. Here's a mental model to help you understand it:
1. Your Application Code
* Use the Julep SDK in your application to define agents, tasks, and workflows
* SDK provides functions and classes for easy setup and management of components
2. Julep Backend Service
* SDK communicates with Julep backend over the network
* Backend handles task execution, session state, document storage, and workflow orchestration
3. Integration with Tools and APIs
* Integrate external tools and services within your workflows
* Backend facilitates integrations for web searches, database access, third-party API calls, and more
To help you visualize this, here's a diagram:
## Core Components
The main orchestrator of your application, backed by foundation models like GPT4 or Claude.
Users can be associated with sessions and are used to scope memories formed by agents.
The main workhorse for Julep apps.
Programmatic interfaces that foundation models can "call" with inputs.
Collections of text snippets indexed into a vector database.
Github Actions-style workflows for complex operations.
Encrypted key-value pairs for securely storing sensitive information like API keys and credentials.
## Infrastructure Components
The document store provides a vector database for semantic search along with a document management system. It supports various file types and handles automatic indexing and retrieval of documents.
The task execution engine handles distributed task processing, state management, error handling with retries, and supports parallel execution of tasks.
The secrets store provides encrypted storage for sensitive information like API keys, credentials, and tokens. It uses AES-256 encryption and developer-scoped access controls to securely manage confidential data.
The API layer provides RESTful API endpoints, SDK support, authentication and authorization capabilities, and handles rate limiting and quotas.
## Data Flow
The data flow in Julep is divided into three main components, each handling specific aspects of request processing and response generation.
* SDK or API calls initiate requests
* Authentication and validation
* Request routing
* Task decomposition
* Step execution
* State management
* Tool integration
* Result aggregation
* Error handling
* Client notification
* State persistence
## Security
- **1.** API key-based access
- **2.** Role-based permissions
- **3.** Session management
- **4.** Token validation
- **1.** Encryption at rest
- **2.** Secure communication
- **3.** Data isolation
- **4.** Access controls
- **5.** Encrypted secrets
- **1.** Audit logging
- **2.** Performance metrics
- **3.** Error tracking
- **4.** Usage analytics
## Conclusion
The modular design enables you to customize and extend functionality as needed, while the security-first approach ensures your data and operations remain protected. Whether you're building a simple chatbot or a complex AI workflow, this architecture provides the foundation you need.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Chat Features in Julep
Source: https://docs.julep.ai/advanced/chat
Learn about the robust chat system and its various features for dynamic interaction with agents
## Overview
Julep provides a robust chat system with various features for dynamic interaction with agents. Here's an overview of the key components and functionalities.
## Features
The chat API allows for the use of tools, enabling the agent to perform actions or retrieve information during the conversation.
You can specify different agents within the same session using the `agent` parameter in the chat settings.
Control the output format, including options for JSON responses with specific schemas.
Configure how the session accesses and stores conversation history and memories.
The API returns information about documents referenced during the interaction, useful for providing citations or sources.
Prerequisites for Using Chat API
- Session Creation: Before using the chat API, you must create a session first. Learn more about the session object on the Session page.
- Document (RAG) Integration: To use Document (RAG) capabilities with the chat API, create a session with the
recall\_options parameter configured with appropriate search parameters. For details on configuring recall\_options, see the Session: Recall Options documentation.
## Input Structure
* **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**: Controls how the session accesses history and memories.(`recall` parameter)
* **Additional Parameters**: Various parameters to control the behavior of the chat. You can find more details in the [Additional Parameters](#additional-parameters) section.
Here's an example of how a typical message object might be structured in a chat interaction:
```python Python theme={"dark"}
"""
Attributes for the Message object:
role (Literal["user", "assistant", "system", "tool"]): The role of the message sender.
tool_call_id (str | None): Optional identifier for a tool call associated with this message.
content (Annotated[str | list[str] | list[Content | ContentModel7 | ContentModel] | None, Field(...)]): The main content of the message, which can be a string, a list of strings, or a list of content models.
name (str | None): Optional name associated with the message.
tool_calls (list[ChosenFunctionCall | ChosenComputer20241022 | ChosenTextEditor20241022 | ChosenBash20241022] | None): List of tool calls generated during the message creation, if any.
"""
# Example of a simple message structure
messages = [{"role": "user", "content": "Your query here"}]
```
This object represents a message in the chat system, detailing the structure and types of data it can hold.
## Additional Parameters
| Parameter | Type | Description | Default |
| -------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `stream` | `bool` | Indicates if the server should stream the response as it's generated. | `False` |
| `stop` | `list[str]` | Up to 4 sequences where the API will stop generating further tokens. | `[]` |
| `seed` | `int` | If specified, the system will make a best effort to sample deterministically for that particular seed value. | `None` |
| `max_tokens` | `int` | The maximum number of tokens to generate in the chat completion. | `None` |
| `logit_bias` | `dict[str, float]` | Modify the likelihood of specified tokens appearing in the completion. | `None` |
| `response_format` | `str` | Response format (set to `json_object` to restrict output to JSON). | `None` |
| `agent` | `UUID` | Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) | `None` |
| `repetition_penalty` | `float` | Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. | `None` |
| `length_penalty` | `float` | Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. | `None` |
| `min_p` | `float` | Minimum probability compared to leading token to be considered. | `None` |
| `frequency_penalty` | `float` | Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. | `None` |
| `presence_penalty` | `float` | Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. | `None` |
| `temperature` | `float` | What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. | `None` |
| `top_p` | `float` | An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top\_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. | `1.0` |
| `recall` | `bool` | Whether to use the document (RAG) search or not | `True` |
| `save` | `bool` | Whether this interaction should be stored in the session history or not | `True` |
| `remember` | `bool` | DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) | `False` |
| `model` | `str` | The model to use for the chat completion. | `None` |
| `metadata` | `dict[str, Any]` | Custom metadata that can be passed to the system template for dynamic behavior. See [System Templates](/advanced/system-templates) for details. | `None` |
| `auto_run_tools` | `bool` | Whether to automatically execute tools and send the results back to the model (requires tools on the agent) | `False` |
| `recall_tools` | `bool` | Whether to include tool requests and responses when recalling messages from history | `True` |
## Usage
Here's an example of how to use the chat API in Julep using the SDKs:
To use the Chat endpint, you always have to create a session first.
```python Python [expandable] theme={"dark"}
# Create a session with custom recall options
client.sessions.create(
agent=agent.id,
user=user.id,
recall=True,
recall_options={
"mode": "hybrid", # or "vector", "text"
"num_search_messages": 4, # number of messages to search for documents
"max_query_length": 1000, # maximum query length
"alpha": 0.7, # weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
"confidence": 0.6, # confidence cutoff level (ranges from -1 to 1)
"limit": 10, # limit of documents to return
"lang": "en-US", # language to be used for text-only search
"metadata_filter": {}, # metadata filter to apply to the search
"mmr_strength": 0, # MMR Strength (ranges from 0 to 1)
}
)
# Chat in the session
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Tell me about Julep"
}
],
recall=True
)
print("Agent's response:", response.choices[0].message.content)
print("Searched Documents:", response.docs)
# Chat with metadata for dynamic system prompts
response_with_metadata = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Help me with Python"
}
],
metadata={
"language": "Spanish",
"expertise_level": "beginner",
"custom_instructions": "Use simple examples"
}
)
# The metadata will be available in the system template as {{metadata.language}}, etc.
# Chat with structured JSON response
response_json = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Analyze the sentiment of: 'I love using Julep, it makes AI development so much easier!'"
}
],
response_format={"type": "json_object"},
# When using response_format, instruct the model to return JSON
settings={
"temperature": 0.3
}
)
# Response will be valid JSON that can be parsed
import json
sentiment_data = json.loads(response_json.choices[0].message.content)
```
```javascript Node.js [expandable] theme={"dark"}
client.sessions.create({
agent: agent.id,
user: user.id,
recall: true,
recall_options: {
mode: "hybrid", // or "vector", "text"
num_search_messages: 4, // number of messages to search for documents
max_query_length: 1000, // maximum query length
alpha: 0.7, // weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
confidence: 0.6, // confidence cutoff level (ranges from -1 to 1)
limit: 10, // limit of documents to return
lang: "en-US", // language to be used for text-only search
metadata_filter: {}, // metadata filter to apply to the search
mmr_strength: 0, // MMR Strength (ranges from 0 to 1)
}
});
// Chat in the session
const response = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "Tell me about Julep"
}
],
recall: true
});
// Chat with metadata for dynamic system prompts
const responseWithMetadata = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "Help me with Python"
}
],
metadata: {
language: "Spanish",
expertise_level: "beginner",
custom_instructions: "Use simple examples"
}
});
// The metadata will be available in the system template as {{metadata.language}}, etc.
// Chat with structured JSON response
const responseJson = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "Analyze the sentiment of: 'I love using Julep, it makes AI development so much easier!'"
}
],
response_format: { type: "json_object" },
// When using response_format, instruct the model to return JSON
settings: {
temperature: 0.3
}
});
// Response will be valid JSON that can be parsed
const sentimentData = JSON.parse(responseJson.choices[0].message.content);
```
To learn more about the Session object, check out the [Session](/concepts/sessions) page.
Check out the [API reference](/api-reference/sessions/chat) or SDK reference ([Python](/sdks/python/reference#sessions) or [JavaScript](/sdks/nodejs/reference#sessions)) for more details on different operations you can perform on sessions.
## Response
* **Content-Type**: `application/json`
* **Body**: A `MessageChatResponse` object containing the full generated message(s)
* **Content-Type**: `text/event-stream`
* **Body**: A stream of `ChatOutputChunk` objects sent as Server-Sent Events (SSE)
To enable streaming, set `stream: true` in your chat request:
```python Python theme={"dark"}
from julep import AsyncClient
# Streaming requires async client
async_client = AsyncClient(api_key="your-api-key")
response = await async_client.sessions.chat(
session_id=session.id,
messages=[{"role": "user", "content": "Tell me a story"}],
stream=True
)
async for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
```
```javascript Node.js theme={"dark"}
const response = await client.sessions.chat({
session_id: session.id,
messages: [{ role: "user", content: "Tell me a story" }],
stream: true
});
for await (const chunk of response) {
process.stdout.write(chunk.choices[0].delta.content);
}
```
Streaming is not available when `auto_run_tools=true`. The system will return an error if both options are enabled.
Both types of responses include the following fields:
* `id`: The unique identifier for the chat response
* `choices`: An object of generated message completions containing:
* `role`: The role of the message (e.g. "assistant", "user", etc.)
* `id`: Unique identifier for the message
* `content`: list of actual message content
* `created_at`: Timestamp when the message was created
* `name`: Optional name associated with the message
* `tool_call_id`: Optional ID referencing a tool call
* `tool_calls`: Optional list of tool calls made during message generation
* `created_at`: When this resource was created as UTC date-time
* `docs`: List of document references used for this request, intended for citation purposes
* `jobs`: List of UUIDs for background jobs that may have been initiated as a result of this interaction
* `usage`: Statistics on token usage for the completion request
## Automatic Tool Calling
Julep supports automatic tool execution during chat interactions. This feature allows tools to be executed seamlessly without manual intervention, making conversations more fluid and responsive.
### How It Works
1. **When `auto_run_tools=true`**:
* The model identifies when a tool should be used based on the conversation
* The tool is automatically executed by Julep's backend
* Results are fed back to the model to continue the conversation
* The entire process happens in a single API call
2. **When `auto_run_tools=false` (default)**:
* The model returns tool call requests in the response
* Your application must execute the tools manually
* Results need to be sent back in a follow-up message
### Example with Automatic Tool Execution
```python Python theme={"dark"}
# Agent with a weather tool
agent = client.agents.create(
name="Weather Assistant",
tools=[
{
"name": "get_weather",
"type": "integration",
"integration": {"provider": "weather"}
}
]
)
# Chat with automatic tool execution
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "What's the weather like in Tokyo and Paris?"
}
],
auto_run_tools=True # Tools execute automatically
)
# The response already includes the weather data
print(response.choices[0].message.content)
# Output: "In Tokyo, it's currently 22°C with clear skies.
# In Paris, it's 15°C with light rain."
```
```javascript Node.js theme={"dark"}
// Agent with a weather tool
const agent = await client.agents.create({
name: "Weather Assistant",
tools: [
{
name: "get_weather",
type: "integration",
integration: {provider: "weather"}
}
]
});
// Chat with automatic tool execution
const response = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "What's the weather like in Tokyo and Paris?"
}
],
auto_run_tools: true // Tools execute automatically
});
// The response already includes the weather data
console.log(response.choices[0].message.content);
// Output: "In Tokyo, it's currently 22°C with clear skies.
// In Paris, it's 15°C with light rain."
```
### Tool History Management
The `recall_tools` parameter controls whether tool calls and their results are included when recalling conversation history:
* **`recall_tools=true` (default)**: Tool interactions are preserved in the conversation history
* **`recall_tools=false`**: Tool calls and results are excluded from recalled messages
This is useful when you want to maintain a cleaner conversation history without the technical details of tool executions.
## Finish Reasons
Natural stop point or provided stop sequence reached
Maximum number of tokens specified in the request was reached
Content was omitted due to a flag from content filters
The model called a tool
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Files (Multimedia)
Source: https://docs.julep.ai/advanced/files
Learn about file handling and management in Julep
## Overview
Julep’s Files feature supports uploading, storing, retrieving, and integrating files across Julep components. It handles various file types and formats.
## File Properties
Each file in Julep has the following properties:
```typescript theme={"dark"}
id: string; // Unique identifier
name: string; // Name of the file
content: string; // Base64 encoded content
created_at: string; // Creation timestamp
size: number; // File size in bytes
hash: string; // File hash for verification
description?: string; // Optional description
mime_type?: string; // Optional MIME type
```
## Basic Operations
### Creating Files
Here's how you can create files using the SDKs:
```python Python theme={"dark"}
from julep import Julep
client = Julep()
file = client.files.create(
name="example.pdf",
content="base64_encoded_content",
description="Sample document",
mime_type="application/pdf"
)
```
```javascript Node.js theme={"dark"}
import Julep from '@julep/sdk';
const client = new Julep();
const file = await client.files.create({
name: "example.pdf",
content: "base64_encoded_content",
description: "Sample document",
mime_type: "application/pdf"
});
```
### Retrieving Files
Here's how you can retrieve files using the SDKs:
```python Python theme={"dark"}
file = client.files.get("file_id")
```
```javascript Node.js theme={"dark"}
const file = await client.files.get("file_id");
```
### Deleting Files
```python Python theme={"dark"}
client.files.delete("file_id")
```
```javascript Node.js theme={"dark"}
await client.files.delete("file_id");
```
## API Reference
For complete API documentation, see [Files API Reference](https://dev.julep.ai/api/docs#tag/files).
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Execution Lifecycle
Source: https://docs.julep.ai/advanced/lifecycle
Understanding the Lifecycle of Task when executed in Julep
## Overview
Understanding the lifecycle of a Julep task is crucial for building effective workflows.
Let's explore this through the lens of a [Trip Plan task](/tutorials/trip-planning) - a practical example that showcases key aspects of task execution.
The Trip Plan task is an ideal example because it demonstrates:
1. Parallel execution of workflow steps
2. Integration with multiple external APIs:
* [Weather API](/integrations/communicationdata/weather) for current conditions
* [Brave Search API](/integrations/search/brave) for attractions
3. Data aggregation from various sources
4. AI-powered personalization of results
Below is a visual representation of how this task flows through Julep's execution lifecycle:
Let's examine each phase of the execution lifecycle using this task as our guide.
This is a simplified representation of the execution lifecycle. In practice, Julep handles many more details and edge cases to ensure robust and efficient task execution.
Out here in this section, we will be focusing on the main workflow steps of the Trip Plan task and how these steps are executed in Julep.
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
```
This schema specifies that our task expects an array of location strings (e.g., `["New York", "London", "Paris"]`).
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: {openweathermap_api_key}
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: {brave_api_key}
```
We're using two integrations:
* The `weather` integration to fetch current weather conditions
* The `brave` search integration to find tourist attractions
### 3. Main Workflow Steps
#### Step 1: Fetch Weather Data
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
```
This step:
* Iterates over each location in the input array
* Calls the weather API for each location
* The `_` represents the current location in the iteration
#### Step 2: Search for Tourist Attractions
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
```
This step:
* Iterates over the locations again
* Searches for tourist attractions in each location
* Constructs a search query by concatenating "tourist attractions in " with the location
#### Step 3: Combine Data
```yaml theme={"dark"}
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
```
This step:
* Combines the data from previous steps into tuples
* Each tuple contains: (location, weather\_data, attractions\_data)
* Uses Python's `zip` function to align the data
#### Step 4: Generate Itineraries
```yaml [expandable] theme={"dark"}
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: {_[0]}
Weather: {_[1]}
Attractions: {_[2]}'''
unwrap: true
```
This step:
* Processes up to 3 locations in parallel
* For each location tuple:
* Sends a prompt to the AI model
* Includes location, weather, and attraction data
* Generates a personalized itinerary
#### Step 5: Format Final Output
```yaml theme={"dark"}
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
```
This step:
* Combines all itineraries into a single output
* Separates each itinerary with a divider
## Example Output
An example output when running this task with an input of `["New York", "London", "Paris", "Tokyo", "Sydney"]`:
Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions.
### Day 1: Iconic Landmarks and Observation Decks
* **Morning:**
* **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.
* **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)
* **Afternoon:**
* **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.
* **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.
* **Evening:**
* **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.
### Day 2: Culture and History
* **Morning:**
* **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.
* **Afternoon:**
* **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.
* **Evening:**
* **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.
### Day 3: Historical and Educational
* **Morning:**
* **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.
* **Afternoon:**
* **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.
* **Evening:**
* **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.
### Additional Tips:
* **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.
* **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.
* **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.
For more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/).
## Enjoy your trip to New York City!
**Day 1: Exploring Iconic London Landmarks**
**Morning:**
1. **Buckingham Palace**
* Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.
* Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.
**Midday:**
2\. **Westminster Abbey**
* Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.
3. **Lunch at Borough Market**
* Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.
**Afternoon:**
4\. **London Eye**
* After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.
**Evening:**
5\. **The Globe Theatre**
* As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.
**Day 2: Museums and Cultural Exploration**
**Morning:**
1. **The British Museum**
* Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.
**Midday:**
2\. **Lunch near Covent Garden**
* Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.
**Afternoon:**
3\. **The Tower of London**
* After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.
**Evening:**
4\. **Tower Bridge**
* Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.
**Day 3: Leisure and Local Experiences**
**Morning:**
1. **Natural History Museum**
* Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.
**Midday:**
2\. **Lunch at South Kensington**
* Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.
**Afternoon:**
3\. **Hyde Park**
* Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.
**Evening:**
4\. **Dinner and a Show in the West End**
* End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.
**Additional Tips:**
* Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.
* London’s public transport system is efficient; consider getting an Oyster card for convenient travel.
* Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.
***
Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:
### Day 1: Embrace the Iconic Landmarks
**Morning: Eiffel Tower**
* **Time:** 9:00 AM
* **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.
* **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)
**Afternoon: Louvre Museum**
* **Time:** 1:00 PM
* **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.
* **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)
**Evening: Seine River Cruise**
* **Time:** 6:00 PM
* **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.
### Day 2: Explore Cultural and Historical Treasures
**Morning: Notre-Dame Cathedral**
* **Time:** 9:30 AM
* **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.
**Afternoon: Musée d'Orsay**
* **Time:** 1:30 PM
* **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.
**Evening: Montmartre and Sacré-Cœur**
* **Time:** 5:00 PM
* **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.
### Day 3: Discover Hidden Gems and Local Flavors
**Morning: Le Marais District**
* **Time:** 10:00 AM
* **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.
**Afternoon: Palais Garnier (Opera House)**
* **Time:** 2:00 PM
* **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.
**Evening: Moulin Rouge Show**
* **Time:** 8:00 PM
* **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.
### Additional Tips:
* **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.
* **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.
* **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.
## Enjoy your snowy adventure in Paris!
**Tokyo Itinerary**
**Day 1: Arrival and Exploration of Historical and Cultural Sites**
* **Morning:**
* **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.
* **Afternoon:**
* **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.
* **Evening:**
* **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.
**Day 2: Modern Tokyo and Unique Experiences**
* **Morning:**
* **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.
* **Afternoon:**
* **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.
* **Evening:**
* **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.
**Day 3: Relax and Explore Green Spaces**
* **Morning:**
* **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.
* **Afternoon:**
* **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.
* **Evening:**
* **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.
**Weather Considerations:**
* With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.
* Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.
**Additional Tips:**
* Always check the opening hours of attractions and book tickets in advance where necessary.
* Use Tokyo’s efficient public transport to move around easily.
* Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.
***
Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.
### Morning
**9:00 AM - Sydney Opera House**
* Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**11:00 AM - Royal Botanic Garden Sydney**
* Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.
### Afternoon
**12:30 PM - Lunch at Opera Bar**
* Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**2:00 PM - Sydney Harbour Bridge**
* After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.
**4:00 PM - The Rocks**
* Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.
### Evening
**6:00 PM - Darling Harbour**
* Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.
**8:00 PM - Dinner at a Local Restaurant**
* Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.
### Additional Suggestions
* If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.
With clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Local Setup
Source: https://docs.julep.ai/advanced/localsetup
Learn how to run Julep locally
## Overview
Juelp is designed to be run locally with the help of Docker. This guide will help you set up Julep locally.
## Prerequisites
* [Docker](https://docs.docker.com/get-docker/)
## Setup Instructions
### 1. Clone the Repository
Clone the repository from your preferred source:
```bash theme={"dark"}
git clone
```
### 2. Navigate to the Root Directory
Change to the root directory of the project:
```bash theme={"dark"}
cd
```
### 3. Set Up Environment Variables
* Create a `.env` file in the root directory.
* Refer to the [`.env.example`](https://github.com/julep-ai/julep/blob/dev/.env.example) file for a list of required variables.
* Ensure that all necessary variables are set in the `.env` file.
### 4. Create a Docker Volume for Backup
Create a Docker volume named `grafana_data`, `memory_store_data`, `temporal-db-data`, `prometheus_data` and `seadweedfs_data`:
```bash theme={"dark"}
docker volume create grafana_data
docker volume create memory_store_data
docker volume create temporal-db-data
docker volume create prometheus_data
docker volume create seaweedfs_data
```
The volumes are used to store the data for the Grafana, Memory Store (Timescale DB), Temporal DB, Prometheus, and SeadweedFS, and Memories respectively.
### 5. Run the Project using Docker Compose
You can run the project in two different modes: **Single Tenant** or **Multi-Tenant**. Choose one of the following commands based on your requirement:
#### Single-Tenant Mode
Run the project in single-tenant mode:
```bash theme={"dark"}
docker compose --env-file .env --profile temporal-ui --profile single-tenant --profile self-hosted-db --profile blob-store --profile temporal-ui-public up --build --force-recreate --watch
```
> **Note:** In single-tenant mode, you can interact with the SDK directly without the need for the API KEY.
#### Multi-Tenant Mode
Run the project in multi-tenant mode:
```bash theme={"dark"}
docker compose --env-file .env --profile temporal-ui --profile multi-tenant --profile self-hosted-db --profile blob-store --profile temporal-ui-public up --build --force-recreate --watch
```
> **Note:** In multi-tenant mode, you need to generate a JWT token locally that act as an API KEY to interact with the SDK.
### 6. Generate a JWT Token (Only for Multi-Tenant Mode)
To generate a JWT token, `jwt-cli` is required. install it from [here](https://github.com/mike-engel/jwt-cli).
Use the following command and replace `JWT_SHARED_KEY` with the corresponding key from your `.env` file to generate a JWT token:
```bash theme={"dark"}
jwt encode --secret JWT_SHARED_KEY --alg HS512 --exp=$(date -j -v +10d +%s) --sub '00000000-0000-0000-0000-000000000000' '{}'
```
> **Note:** This command generates a JWT token that will be valid for 10 days. adjust the expiration date as needed.
### 7. Access and Interact
* **Temporal UI**: You can access the Temporal UI through the specified port in your `.env` file.
* **API Interactions**: Depending on the chosen mode, interact with the setup using the provided endpoints.
### Troubleshooting
* Ensure that all required Docker images are available.
* Check for missing environment variables in the `.env` file.
* Use the `docker compose logs` command to view detailed logs for debugging.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Multi-Agent Multi-User Sessions
Source: https://docs.julep.ai/advanced/multi-agent-multi-user-sessions
Learn how to manage complex multi-agent systems with multiple users
## Overview
Julep supports different types of sessions based on the number of agents and users involved. This flexibility allows for complex interactions and use cases.
## Types of Sessions
- No user
- Single user
- Multiple users
- No user
- Single user
- Multiple users
## Behavior in Multi-Agent/User Sessions
### User Behavior
- No user data is retrieved
- (Upcoming) Memories are not mined from the session
- Docs, metadata, memories, etc. are retrieved for all users in the session
- Messages can be added for each user by referencing them by name in the `ChatML` messages
- (Upcoming) Memories mined in the background are added to the corresponding user's scope
### Agent Behavior
- Works as expected
- 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 specify which `agent` to use in a request, in which case, only that agent will be used
This multi-agent/user capability allows for sophisticated scenarios such as:
* Collaborative problem-solving with multiple AI agents
* Group conversations with multiple users and agents
* Specialized agents working together on complex tasks
## Conclusion
By supporting these various configurations, Julep provides a flexible framework for building diverse and powerful AI applications.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# New Syntax (Important)
Source: https://docs.julep.ai/advanced/new-syntax
Learn about the new workflow syntax and how to migrate your existing workflows
## Overview
We've updated our workflow syntax to make data flow more explicit and consistent.
These changes improve readability and make workflows more maintainable.
While this update requires modifications to existing workflows, it provides a more robust foundation for workflow development.
## 1. Input/Output Data References
The most significant change is how we reference input and output data between workflow steps. The new syntax makes the data flow more explicit by using the `steps` keyword.
### Old Syntax:
* `inputs[x]` - Referenced input data from step x
* `outputs[x]` - Referenced output data from step x
* `_x` - Shorthand for input data from step x
### New Syntax:
* `steps[x].input` - References input data from step x
* `steps[x].output` - References output data from step x
* `_x` - Has been removed
### Example
#### Old Syntax
```yaml theme={"dark"}
- evaluate:
input_data: inputs[0].a
output_data: outputs[0].b
shorthand: _0.c
```
#### New Syntax
```yaml theme={"dark"}
- evaluate:
input: $ steps[0].input.a
output: $ steps[0].output.b
shorthand: $ steps[0].input.c
```
Notice the addition of the `$` prefix in the new syntax. This indicates that the value should be treated as a Python expression. We'll cover this in detail in the next section.
### Key Changes:
* `inputs[0]` becomes `steps[0].input`
* `outputs[0]` becomes `steps[0].output`
* `_0` becomes `steps[0].input`
* Added `$` prefix to indicate Python expressions
## 2. Template Syntax Changes Inside `prompt` Steps
Another significant change is the removal of Jinja templates (`{{ }}`) in `log` steps and `prompt` steps. Instead, we now use Python f-strings with the `$` prefix for dynamic content.
### Old Syntax (Using Jinja):
```yaml theme={"dark"}
- prompt:
- role: user
content: Write me an article about the topic: {{inputs[0].topic}}
```
### New Syntax (Using f-strings):
```yaml theme={"dark"}
- prompt:
- role: user
content: $ f"Write me an article about the topic: {steps[0].input.topic}"
- role: user
content: this is only text, no need for dollar sign prefix
```
For multiple lines, you can use a multiline f-string:
```yaml theme={"dark"}
- prompt:
- role: user
content: |-
$ f"""
Write me an article about the topic: {steps[0].input.topic}.
The article should be {steps[0].input.article_length} words long.
"""
```
The `$` prefix is only needed when the value contains a Python expression or f-string. Plain text content doesn't require the prefix.
## 3. When to Use the \$ Prefix
The `$` prefix should be used in two scenarios:
1. When referencing step data (inputs/outputs)
2. When using Python expressions or f-strings
#### Examples:
```yaml theme={"dark"}
steps:
- prompt:
# Needs $ because it uses an f-string
- role: user
content: $ f"The temperature is {steps[0].input.temp}°C"
# No $ needed - plain text
- role: user
content: Tell me about the weather
- evaluate:
# Needs $ because it references step data
input: $ steps[0].output
# Needs $ because it's a Python expression
condition: $ len(steps[0].input.text) > 100
```
# Python Expression
Source: https://docs.julep.ai/advanced/python-expression
Learn how to use Python expressions in Julep task definitions
## Overview
Julep tasks support Python expressions for dynamic value computation and data manipulation. This guide explains how to use them effectively.
## The Special `_` Variable
The underscore `_` is a special variable that serves three different purposes depending on where it's used:
1. **First Step Input**: In the first step of a task, `_` contains the execution input:
```yaml theme={"dark"}
# If task is executed with input `{"topic": "AI"}`
- evaluate:
topic: $ _.topic # Accesses the input "AI"
```
2. **Previous Step Output**: In any subsequent step, `_` contains the output from the previous step:
```yaml theme={"dark"}
- evaluate:
results: $ _.split('\n') # Splits previous step's output into lines
```
3. **Iterator Value**: In `foreach` and `map` steps, `_` represents the current item being iterated.
```yaml theme={"dark"}
# If the task is executed with input `{"questions": ["What is AI?", "What is Julep?"]}`
- foreach:
in: $ _.questions
do:
- wait_for_input:
info:
"message": $ _ # _ is each question
```
## The Special `$` Variable
The `$` variable is used to differentiate between a Python expression and a string. When using the `$` variable, the expression is evaluated as a Python expression.
```yaml theme={"dark"}
- evaluate:
topic: $ _.topic # Accesses the input "AI"
```
```yaml theme={"dark"}
- prompt:
- role: user
content: |-
$ f'''
Please answer the following question:
{_.question}
'''
```
To learn more about how to use the `$` variable, please refer to the [New Syntax](/advanced/new-syntax) section.
## Where Python Expressions Are Used
Python expressions can be used in various task steps. For a complete list of step types and their syntax, refer to the [Step Types](/advanced/types-of-task-steps) page.
Common places include:
* `evaluate` steps
* Tool `arguments`
* `if` conditions
* `foreach` and `map` iterations
## Available Functions and Libraries
The following Python functions and libraries are available for use in expressions:
### Basic Python Builtins
* `abs`, `all`, `any`, `bool`, `dict`, `enumerate`
* `float`, `int`, `len`, `list`, `map`, `max`, `min`
* `round`, `set`, `str`, `sum`, `tuple`, `zip`, `reduce`
### Safe Versions of Functions
* `range`: `def safe_range(*args)`
Safely creates a range object with size limits (max 1,000,000 elements).
* `load_json`: `def safe_json_loads(s: str) -> Any` (Deprecated in favor of `json.loads`)
Safely parses a JSON string with size limits.
* `load_yaml`: `def safe_yaml_load(s: str) -> Any` (Deprecated in favor of `yaml.safe_load`)
Safely parses a YAML string with size limits.
* `dump_json`: `def dump_json(obj: Any, *, **kwargs) -> str` (Deprecated in favor of `json.dumps`)
Safely serializes an object to a JSON string.
* `dump_yaml`: `def dump_yaml(obj: Any, **kwargs) -> str` (Deprecated in favor of `yaml.dump`)
Safely serializes an object to a YAML string.
* `extract_json`: `def safe_extract_json(string: str) -> Any`
Safely extracts and parses JSON from text.
### Regex and NLP Functions
* `search_regex`: `def search_regex(pattern: str, string: str) -> Optional[re2.Match]`
Searches for a regex pattern in a string.
* `match_regex`: `def match_regex(pattern: str, string: str) -> bool`
Checks if a regex pattern matches a string.
* `chunk_doc`: `def chunk_doc(string: str) -> list[str]`
Chunks a string into sentences.
* `nlp` pipelines.
Example using these functions in an evaluate step:
````yaml theme={"dark"}
- evaluate:
# Parse JSON string with size limit of 1MB
data: $ json.loads(_.json_string)
# Parse YAML string with size limit of 1MB
config: $ yaml.safe_load(_.yaml_string)
# Extract JSON from text that might contain markdown code blocks
extracted_content: $ extract_json('Here is some JSON: ```json\n{"key": "value"}\n```')
````
### CSV Functions
* `reader`: `def reader(data: str, dialect="excel", delimiter: str = ",", quotechar: str | None = '"', escapechar: str | None = None, doublequote: bool = True, skipinitialspace: bool = False, lineterminator: str = "\r\n", quoting=0, strict: bool = False) -> csv._reader`
Creates a CSV reader.
* `writer`: `def writer(data: str, dialect="excel", delimiter: str = ",", quotechar: str | None = '"', escapechar: str | None = None, doublequote: bool = True, skipinitialspace: bool = False, lineterminator: str = "\r\n", quoting=0, strict: bool = False) -> csv._writer`
Creates a CSV writer.
* `DictReader`: `class DictReader(data: str, fieldnames=None, restkey=None, restval=None, dialect="excel", *args, **kwds)`
Create an object that operates like a regular reader but maps the information in each row to a dict.
* `DictWriter`: `class DictWriter(data: str, fieldnames, restval="", extrasaction="raise", dialect="excel", *args, **kwds)`
Create an object which operates like a regular writer but maps dictionaries onto output rows.
* `register_dialect`: `def register_dialect(name: str, dialect: type[Dialect] = ..., *, delimiter: str = ",", quotechar: str | None = '"', escapechar: str | None = None, doublequote: bool = True, skipinitialspace: bool = False, lineterminator: str = "\r\n", quoting: _QuotingType = 0, strict: bool = False) -> csv._reader`
Associate dialect with name.
* `unregister_dialect`: `def unregister_dialect(name: str) -> None`
Delete the dialect associated with name from the dialect registry.
* `get_dialect`: `def get_dialect(name: str) -> Dialect`
Return the dialect associated with name.
* `list_dialects`: `def list_dialects() -> list[str]`
Return the names of all registered dialects.
* `field_size_limit`: `def field_size_limit(new_limit: int = ...) -> int`
Returns the current maximum field size allowed by the parser.
* `Dialect`: `class Dialect()`
The Dialect class is a container class whose attributes contain information for how to handle doublequotes, whitespace, delimiters, etc.
* `excel`: `class excel()`
The excel class defines the usual properties of an Excel-generated CSV file.
* `excel_tab`: `class excel_tab()`
The excel\_tab class defines the usual properties of an Excel-generated TAB-delimited file.
* `unix_dialect`: `class unix_dialect()`
The unix\_dialect class defines the usual properties of a CSV file generated on UNIX systems, i.e. using '\n' as line terminator and quoting all fields.
* `Sniffer`: `class Sniffer`
The Sniffer class is used to deduce the format of a CSV file.
* `QUOTE_ALL` - Instructs writer objects to quote all fields.
* `QUOTE_MINIMAL` - Instructs writer objects to only quote those fields which contain special characters such as delimiter, quotechar or any of the characters in lineterminator.
* `QUOTE_NONNUMERIC` - Instructs writer objects to quote all non-numeric fields. Instructs reader objects to convert all non-quoted fields to type float.
* `QUOTE_NONE` - Instructs writer objects to never quote fields.
* `QUOTE_NOTNULL` - Instructs writer objects to quote all fields which are not None.
* `QUOTE_STRINGS` - Instructs writer objects to always place quotes around fields which are strings.
* `Error`: `class Error()`
Raised by any of the functions when an error is detected.
This module tries to mimic `csv` module from the standard Python library. You can find additional information [here](https://docs.python.org/3/library/csv.html)
## Example Usage
```yaml theme={"dark"}
name: Data Processing Task
main:
# Using _ as input
- evaluate:
csv_data: $ [row for row in csv.reader("a,b,c\n1,2,3")]
```
### Standard Library Modules
* `re`: Regular expressions (using re2)
* `json`: JSON operations
* `yaml`: YAML operations
* `string`: String constants and operations
* `datetime`: Date and time operations
* `math`: Mathematical functions
* `statistics`: Statistical operations
* `base64`: Base64 encoding/decoding
* `urllib.parse`: URL parsing operations
* `random`: Random number generation
* `time`: Time operations
* `csv`: CSV operations
For the complete list of available functions and their safe implementations, refer to the [utils.py](https://github.com/julep-ai/julep/blob/main/agents-api/agents_api/activities/utils.py) file in the source code.
## Example Usage
Here's a practical example combining different aspects of Python expressions:
```yaml theme={"dark"}
name: Data Processing Task
main:
# Using _ as input
- evaluate:
topics: $ _.topics # Access input topics
# Using _ as previous output
- evaluate:
filtered_topics: $ [t for t in _.topics if len(t) > 3]
# Using _ in foreach
- foreach:
in: $ _.filtered_topics
do:
- tool: web_search
arguments:
query: $ 'Latest news about ' + _ # _ is each topic
```
### Custom Implementations
#### Humanization (Alpha)
The `humanize_text_alpha` function transforms text using multiple techniques such as:
* Back-translation.
* Rewriting using non-public LLMs.
* Stylistic modifications such as homoglyphs and em dashes.
Breakdown of the function:
Function signature:
`humanize_text_alpha(text: str, threshold: float = 90, src_lang: str = "english", target_lang: str = "german", use_homoglyphs: bool = True, use_em_dashes: bool = True, grammar_check: bool = False, max_tries: int = 10) -> str`
Parameters:
* `text`: The text to be humanized.
* `threshold`: The threshold for the humanization (0-100).
* `src_lang`: The source language of the original text.
* `target_lang`: The target language used in the back-translation technique.
* `use_homoglyphs`: Whether to use homoglyphs, a technique that replaces certain characters with similar looking characters to trick AI detection classifiers.
* `use_em_dashes`: Whether to use em dashes, i.e. adding dashes between longer words to break the tokens.
* `grammar_check`: Whether to run another prompt after back-translation to check for correct grammar.
* `max_tries`: The maximum number of tries to humanize the text.
Example usage:
```yaml theme={"dark"}
- evaluate:
humanized_text: $ humanize_text_alpha(_.text, threshold=30, target_lang="german")
```
#### Markdown to HTML
The `markdown_to_html` function converts markdown text to HTML.
Breakdown of the function:
Function signature:
`markdown_to_html(markdown_text: str) -> str`
Parameters:
* `markdown_text`: The markdown text to be converted to HTML.
Example usage:
```yaml theme={"dark"}
- evaluate:
html_text: $ markdown_to_html(_.markdown_text)
```
#### HTML to Markdown
The `html_to_markdown` function converts HTML text to markdown.
Breakdown of the function:
Function signature:
`html_to_markdown(html_text: str) -> str`
Parameters:
* `html_text`: The HTML text to be converted to markdown.
Example usage:
```yaml theme={"dark"}
- evaluate:
markdown_text: $ html_to_markdown(_.html_text)
```
## Security
All Python expressions are executed in a sandboxed environment with:
Limited available functions to prevent unsafe operations
Maximum string length restrictions to prevent memory issues
Collection size limits to prevent resource exhaustion
Execution time limits to prevent infinite loops
This ensures safe execution while providing necessary functionality for task workflows.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Render Endpoint in Julep
Source: https://docs.julep.ai/advanced/render
Learn about the render endpoint for previewing chat inputs before sending them to the model
## Overview
Julep provides a render endpoint that allows you to preview how chat inputs will be processed before actually sending them to the model. This is useful for debugging, testing templates, and understanding how the system processes your messages.
## Purpose
The render endpoint processes chat inputs exactly as the chat endpoint would, but stops short of sending them to the model.
See how templates in your messages and system prompts will be rendered with the current environment variables.
Preview which documents will be retrieved from your knowledge base for a given input.
Examine how tools will be formatted and made available to the model.
Useful for debugging complex prompts and understanding the exact input that would be sent to the model.
* The render endpoint uses the same input format as the chat endpoint but returns the processed messages without actually calling the model.
* To use the render endpoint, you need to create a session first. To learn more about the session object, check out the [Session](/concepts/sessions) page.
## Input Structure
The render endpoint accepts the same input structure as the chat endpoint:
* **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**: Controls how the session accesses history and memories (`recall` parameter).
* **Additional Parameters**: Various parameters to control the behavior of the rendering.
Here's an example of how a typical message object might be structured in a render request:
```python Python theme={"dark"}
"""
Attributes for the Message object:
role (Literal["user", "assistant", "system", "tool"]): The role of the message sender.
tool_call_id (str | None): Optional identifier for a tool call associated with this message.
content (Annotated[str | list[str] | list[Content | ContentModel7 | ContentModel] | None, Field(...)]): The main content of the message, which can be a string, a list of strings, or a list of content models.
name (str | None): Optional name associated with the message.
tool_calls (list[ChosenFunctionCall | ChosenComputer20241022 | ChosenTextEditor20241022 | ChosenBash20241022] | None): List of tool calls generated during the message creation, if any.
"""
# Example of a simple message structure
messages = [{"role": "user", "content": "Your query here"}]
```
This object represents a message in the chat system, detailing the structure and types of data it can hold.
## Additional Parameters
The render endpoint accepts the same parameters as the chat endpoint:
| Parameter | Type | Description | Default |
| ----------------- | ----------- | --------------------------------------------------------------------------------------------- | ------- |
| `model` | `str` | The model to use for validation (though no actual model call is made). | `None` |
| `agent` | `UUID` | Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) | `None` |
| `recall` | `bool` | Whether previous memories and docs should be recalled or not. | `True` |
| `response_format` | `str` | Response format specification (used for validation only). | `None` |
| `temperature` | `float` | Not used in rendering but validated for format. | `None` |
| `top_p` | `float` | Not used in rendering but validated for format. | `1.0` |
| `max_tokens` | `int` | Not used in rendering but validated for format. | `None` |
| `stop` | `list[str]` | Not used in rendering but validated for format. | `[]` |
## Response Structure
The render endpoint returns a `RenderResponse` object with the following structure:
```json theme={"dark"}
{
"messages": [
// Array of processed messages that would be sent to the model
],
"docs": [
// Array of document references that would be used for this request
],
"tools": [
// Array of formatted tools that would be available to the model
]
}
```
The render response includes:
* `messages`: The fully processed messages, including rendered templates and system messages.
* `docs`: List of document references that would be used for this request, intended for citation purposes.
* `tools`: The formatted tools that would be available to the model.
## Usage
Here's an example of how to use the render endpoint in Julep using the SDKs:
To use the render endpoint, you always have to create a session first.
```python Python [expandable] theme={"dark"}
# Create a session with custom recall options
client.sessions.create(
agent=agent.id,
user=user.id,
recall=True,
recall_options={
"mode": "hybrid", # or "vector", "text"
"num_search_messages": 4, # number of messages to search for documents
"max_query_length": 1000, # maximum query length
"alpha": 0.7, # weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
"confidence": 0.6, # confidence cutoff level (ranges from -1 to 1)
"limit": 10, # limit of documents to return
"lang": "en-US", # language to be used for text-only search
"metadata_filter": {}, # metadata filter to apply to the search
"mmr_strength": 0, # MMR Strength (ranges from 0 to 1)
}
)
# Render the chat input without sending to the model
render_response = client.sessions.render(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Tell me about Julep"
}
],
recall=True
)
print("Processed messages:", render_response.messages)
print("Retrieved documents:", render_response.docs)
print("Available tools:", render_response.tools)
```
```javascript Node.js [expandable] theme={"dark"}
client.sessions.create({
agent: agent.id,
user: user.id,
recall: true,
recall_options: {
mode: "hybrid", // or "vector", "text"
num_search_messages: 4, // number of messages to search for documents
max_query_length: 1000, // maximum query length
alpha: 0.7, // weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
confidence: 0.6, // confidence cutoff level (ranges from -1 to 1)
limit: 10, // limit of documents to return
lang: "en-US", // language to be used for text-only search
metadata_filter: {}, // metadata filter to apply to the search
mmr_strength: 0, // MMR Strength (ranges from 0 to 1)
}
});
// Render the chat input without sending to the model
const renderResponse = await client.sessions.render({
session_id: session.id,
messages: [
{
role: "user",
content: "Tell me about Julep"
}
],
recall: true
});
```
## Use Cases
Test how your templates will be rendered with the current environment variables.
Preview which documents will be retrieved for a given query.
Verify that tools are properly configured before sending to the model.
Test how system prompts will be processed and combined with user messages.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Secrets Management
Source: https://docs.julep.ai/advanced/secrets-management
Advanced techniques for managing sensitive information in Julep
# Secrets Management
This guide covers advanced topics for managing secrets in Julep, including security architecture, best practices, rotation policies, and integration patterns.
## Security Architecture
Secrets in Julep are stored with a layered security approach:
1. **Application-level Validation**: Secrets are validated before being stored
2. **Database Encryption**: Secrets are stored encrypted using PostgreSQL's pgcrypto extension with AES-256
3. **Access Control**: Secrets are scoped to developers and only accessible within their resources
4. **Master Key Security**: A separate master encryption key secures all stored secrets
The encryption process works as follows:
* When a secret is created, its value is encrypted using the master key
* The encrypted value is stored in the database's `value_encrypted` column
* When a secret is accessed, the value is decrypted using the master key
* The master key is stored as an environment variable, separate from the database
## Creating Effective Secret Names
Secrets should have descriptive names that follow these conventions:
* Use snake\_case formatting
* Begin with a letter and contain only alphanumeric characters and underscores
* Use a prefix to indicate the service (e.g., `aws_secret_key`, `stripe_api_key`)
* Be specific enough to understand the purpose (e.g., `gmail_oauth_token` vs `email_token`)
## Secret Rotation Best Practices
Regular rotation of secrets is a security best practice:
1. Create a new secret with a temporary name
2. Update your services to use the new secret
3. Once confirmed working, delete the old secret
4. Update the new secret's name to the standard name
For automated rotation:
```python [expandable] theme={"dark"}
from julep import Julep
import uuid
client = Julep(api_key="your_api_key")
# Generate temporary name
temp_name = f"stripe_key_rotation_{uuid.uuid4().hex[:8]}"
# Create new secret with temp name
client.secrets.create(
name=temp_name,
value="sk_new_value...",
description="New Stripe API key (rotation)",
metadata={"rotation_date": "2025-05-10"}
)
# Test the new key (implement your validation logic here)
# ...
# If valid, delete old secret and rename new one
client.secrets.delete(name="stripe_api_key")
client.secrets.update(
name=temp_name,
new_name="stripe_api_key",
description="Stripe API key",
metadata={"last_rotated": "2025-05-10"}
)
```
## Using Secrets with Different Tool Types
### API Tools
For HTTP-based tools, reference secrets in the headers or authentication:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: api_service
operation: fetch_data
arguments:
url: "https://api.example.com/data"
headers:
Authorization: "$ f'Bearer {secrets.api_token}'"
```
### Database Connections
For database tools, use secrets for connection credentials:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: database
operation: query
arguments:
query: "SELECT * FROM users LIMIT 10"
secrets:
db_username: "my_db_user"
db_password: "my_db_password"
```
### AI Service Integration
For AI services that require API keys:
```yaml theme={"dark"}
steps:
- kind: prompt
model: "$ secrets.preferred_model"
provider: "$ secrets.provider_name"
prompt: "Generate creative ideas for a marketing campaign"
api_key: "$ secrets.openai_api_key"
```
## Managing Secrets for Multi-Environment Deployments
For applications deployed across development, staging, and production environments:
1. Use consistent naming conventions with environment prefixes:
* `dev_stripe_key`, `staging_stripe_key`, `prod_stripe_key`
2. Use metadata to tag secrets by environment:
```python theme={"dark"}
client.secrets.create(
name="stripe_api_key",
value="sk_test_...",
metadata={"environment": "production"}
)
```
3. Filter secrets by environment when listing:
```python theme={"dark"}
prod_secrets = client.secrets.list(
metadata={"environment": "production"}
)
```
## Secret Templating
For complex configurations that require multiple secrets:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: database
operation: query
arguments:
connection_string: "$ f'mongodb+srv://{secrets.db_username}:{secrets.db_password}@{secrets.db_host}/{secrets.db_name}'"
```
## Securing LLM API Keys with Secrets
Julep automatically looks for LLM API keys in your secrets store based on the provider name. Use these naming conventions for automatic lookup:
| Provider | Secret Name |
| ------------ | ---------------------- |
| OpenAI | `OPENAI_API_KEY` |
| Anthropic | `ANTHROPIC_API_KEY` |
| Google | `GOOGLE_API_KEY` |
| Azure OpenAI | `AZURE_OPENAI_API_KEY` |
| Cohere | `COHERE_API_KEY` |
Example of setting up an LLM API key:
```python theme={"dark"}
client.secrets.create(
name="OPENAI_API_KEY",
value="sk-...",
description="OpenAI API key for GPT-4 access"
)
```
## Audit and Monitoring
Best practices for security monitoring:
1. Regularly audit secret access and usage
2. Track changes to secrets via the `updated_at` timestamp
3. Implement secret expiration for highly sensitive data
4. Use metadata to track last review or rotation dates
Example audit script:
```python theme={"dark"}
from julep import Julep
from datetime import datetime, timedelta
client = Julep(api_key="your_api_key")
# Find secrets not rotated in over 90 days
old_threshold = datetime.now() - timedelta(days=90)
secrets = client.secrets.list()
for secret in secrets.items:
if secret.updated_at < old_threshold:
print(f"WARNING: Secret {secret.name} has not been rotated in over 90 days")
```
## Troubleshooting
Common issues when working with secrets:
1. **Secret Not Found**: Check that the secret name matches exactly, including case
2. **Permission Errors**: Verify the developer ID has access to the secret
3. **Encryption Errors**: Ensure the master key is correctly set in the environment
4. **Reference Errors**: Ensure the secret reference syntax is correct in expressions and templates
## Next Steps
* [Using Secrets in Julep](/guides/using-secrets) - Step-by-step guide for using secrets
* [Integration Patterns](/guides/advanced/integration-patterns) - Learn how to use secrets with integrations
* [API Reference](/api-reference#tag/secrets) - Complete API reference for secrets
# System Templates
Source: https://docs.julep.ai/advanced/system-templates
Learn how to use Jinja2 templates to create dynamic system prompts for your AI agents
System templates allow you to create dynamic, context-aware prompts for your AI agents using Jinja2 templating. This guide covers everything you need to know about creating and customizing system templates.
## Overview
System templates in Julep are **Jinja2 templates** that define the initial system prompt for your AI agents. They allow you to:
* Create dynamic prompts that adapt based on context
* Include user information, session data, and other variables
* Implement conditional logic for different scenarios
* Maintain consistency across agent interactions
* Inject custom metadata at the message level for dynamic behavior
## Template Hierarchy
Julep uses a three-level hierarchy for system templates:
1. **Agent Default Template**: Defined when creating an agent
2. **Session Template**: Can override the agent's default template for specific sessions
3. **Chat Metadata**: Can inject dynamic variables at the individual message level
The session template takes precedence over the agent's default template. Chat metadata is available in both cases.
## Default System Template
When you create an agent without specifying a custom template, Julep uses this default template:
```jinja theme={"dark"}
{%- if agent.name -%}
You are {{agent.name}}.{{" "}}
{%- endif -%}
{%- if agent.about -%}
About you: {{agent.about}}.{{" "}}
{%- endif -%}
{%- if user.name -%}
You are talking to {{user.name}}.{{" "}}
{%- endif -%}
{%- if user.about -%}
About the user: {{user.about}}.{{" "}}
{%- endif -%}
{{agent.instructions}}
{%- if tools -%}
You have access to these tools:
{%- for tool in tools -%}
{%- if tool.type == "function" -%}
- {{tool.function.name}}
{%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}
{%- else -%}
- {{ 0/0 }} {# Error: Other tool types aren't supported yet #}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- if docs -%}
Relevant documents:
{%- for doc in docs -%}
{{doc.title}}
{%- if doc.content -%}:
{{doc.content}}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
```
## Template Variables
The following variables are available in your system templates:
### Core Variables
| Variable | Type | Description |
| -------------------- | ------------ | ------------------------------------- |
| `agent` | Object | The agent's configuration and details |
| `agent.name` | String | The agent's name |
| `agent.about` | String | Description of the agent |
| `agent.instructions` | Array/String | Agent's instructions |
| `user` | Object | Information about the current user |
| `user.name` | String | The user's name |
| `user.about` | String | Description of the user |
| `session` | Object | Current session information |
| `session.situation` | String | The session's context/situation |
### Dynamic Variables
| Variable | Type | Description |
| ---------- | ------ | --------------------------------------- |
| `tools` | Array | Available tools for the agent |
| `docs` | Array | Relevant documents from searches |
| `metadata` | Object | Custom metadata passed in chat requests |
## Creating Custom Templates
### Basic Example
Here's a simple custom template for a customer service agent:
```python python theme={"dark"}
agent = client.agents.create(
name="Customer Support Agent",
model="gpt-4",
about="A helpful customer service representative",
instructions=[
"Be polite and professional",
"Always offer to help further"
],
default_system_template="""
You are {{agent.name}}, a customer service representative.
{%- if metadata.priority == "high" -%}
⚠️ HIGH PRIORITY CUSTOMER - Provide expedited service
{%- endif -%}
{%- if metadata.customer_tier == "premium" -%}
Premium customer - Offer enhanced support options
{%- endif -%}
Your guidelines:
{{agent.instructions}}
{%- if user.name -%}
Customer name: {{user.name}}
{%- endif -%}
"""
)
```
```javascript javascript theme={"dark"}
const agent = await client.agents.create({
name: "Customer Support Agent",
model: "gpt-4",
about: "A helpful customer service representative",
instructions: [
"Be polite and professional",
"Always offer to help further"
],
default_system_template: `
You are {{agent.name}}, a customer service representative.
{%- if metadata.priority == "high" -%}
⚠️ HIGH PRIORITY CUSTOMER - Provide expedited service
{%- endif -%}
{%- if metadata.customer_tier == "premium" -%}
Premium customer - Offer enhanced support options
{%- endif -%}
Your guidelines:
{{agent.instructions}}
{%- if user.name -%}
Customer name: {{user.name}}
{%- endif -%}
`
});
```
### Advanced Example with Conditional Logic
```python python theme={"dark"}
template = """
{%- if agent.name -%}
You are {{agent.name}}.
{%- endif -%}
{# Mood-based personality adjustment #}
{%- if metadata.mood == "friendly" -%}
Be warm, conversational, and use a friendly tone.
{%- elif metadata.mood == "professional" -%}
Maintain a formal, business-appropriate tone.
{%- elif metadata.mood == "playful" -%}
Feel free to be creative and add appropriate humor.
{%- else -%}
Respond in a balanced, helpful manner.
{%- endif -%}
{# Language preferences #}
{%- if metadata.language -%}
Respond in {{metadata.language}}.
{%- endif -%}
{# Expertise areas #}
{%- if metadata.expertise -%}
You are an expert in:
{%- for skill in metadata.expertise -%}
- {{skill}}
{%- endfor -%}
{%- endif -%}
{# Include instructions #}
{{agent.instructions}}
{# Tool availability #}
{%- if tools -%}
Available tools:
{%- for tool in tools -%}
- {{tool.function.name}}: {{tool.function.description}}
{%- endfor -%}
{%- endif -%}
"""
# Using the template with metadata
response = client.sessions.chat(
session_id=session.id,
messages=[
{"role": "user", "content": "Help me with Python"}
],
metadata={
"mood": "friendly",
"language": "English",
"expertise": ["Python", "Machine Learning", "Data Science"]
}
)
```
```javascript javascript theme={"dark"}
const template = `
{%- if agent.name -%}
You are {{agent.name}}.
{%- endif -%}
{# Mood-based personality adjustment #}
{%- if metadata.mood == "friendly" -%}
Be warm, conversational, and use a friendly tone.
{%- elif metadata.mood == "professional" -%}
Maintain a formal, business-appropriate tone.
{%- elif metadata.mood == "playful" -%}
Feel free to be creative and add appropriate humor.
{%- else -%}
Respond in a balanced, helpful manner.
{%- endif -%}
{# Language preferences #}
{%- if metadata.language -%}
Respond in {{metadata.language}}.
{%- endif -%}
{# Expertise areas #}
{%- if metadata.expertise -%}
You are an expert in:
{%- for skill in metadata.expertise -%}
- {{skill}}
{%- endfor -%}
{%- endif -%}
{# Include instructions #}
{{agent.instructions}}
{# Tool availability #}
{%- if tools -%}
Available tools:
{%- for tool in tools -%}
- {{tool.function.name}}: {{tool.function.description}}
{%- endfor -%}
{%- endif -%}
`;
// Using the template with metadata
const response = await client.sessions.chat({
sessionId: session.id,
messages: [
{ role: "user", content: "Help me with Python" }
],
metadata: {
mood: "friendly",
language: "English",
expertise: ["Python", "Machine Learning", "Data Science"]
}
});
```
## Using Chat Metadata
The `metadata` field in chat requests allows you to pass dynamic variables that can be used in your system templates. This enables message-level customization without modifying the agent or session.
### Example: Dynamic Instructions
```python python theme={"dark"}
# Create an agent with a metadata-aware template
agent = client.agents.create(
name="Adaptive Assistant",
default_system_template="""
You are {{agent.name}}.
{%- if metadata.instructions -%}
Special instructions for this conversation:
{{metadata.instructions}}
{%- endif -%}
{%- if metadata.constraints -%}
Important constraints:
{%- for constraint in metadata.constraints -%}
- {{constraint}}
{%- endfor -%}
{%- endif -%}
"""
)
# Use metadata to modify behavior per message
response = client.sessions.chat(
session_id=session.id,
messages=[{"role": "user", "content": "Write a story"}],
metadata={
"instructions": "Write in the style of Edgar Allan Poe",
"constraints": [
"Keep it under 200 words",
"Include a raven",
"End with a twist"
]
}
)
```
```javascript javascript theme={"dark"}
// Create an agent with a metadata-aware template
const agent = await client.agents.create({
name: "Adaptive Assistant",
default_system_template: `
You are {{agent.name}}.
{%- if metadata.instructions -%}
Special instructions for this conversation:
{{metadata.instructions}}
{%- endif -%}
{%- if metadata.constraints -%}
Important constraints:
{%- for constraint in metadata.constraints -%}
- {{constraint}}
{%- endfor -%}
{%- endif -%}
`
});
// Use metadata to modify behavior per message
const response = await client.sessions.chat({
sessionId: session.id,
messages: [{ role: "user", content: "Write a story" }],
metadata: {
instructions: "Write in the style of Edgar Allan Poe",
constraints: [
"Keep it under 200 words",
"Include a raven",
"End with a twist"
]
}
});
```
## Session vs Agent Templates
You can override an agent's default template at the session level:
```python python theme={"dark"}
# Agent with default template
agent = client.agents.create(
name="Multi-purpose Assistant",
default_system_template="You are a helpful assistant."
)
# Session with custom template
session = client.sessions.create(
agent_id=agent.id,
system_template="""
You are a technical writing assistant.
Focus on clarity, accuracy, and proper documentation structure.
{%- if metadata.doc_type -%}
You are writing a {{metadata.doc_type}}.
{%- endif -%}
"""
)
```
```javascript javascript theme={"dark"}
// Agent with default template
const agent = await client.agents.create({
name: "Multi-purpose Assistant",
default_system_template: "You are a helpful assistant."
});
// Session with custom template
const session = await client.sessions.create({
agentId: agent.id,
system_template: `
You are a technical writing assistant.
Focus on clarity, accuracy, and proper documentation structure.
{%- if metadata.doc_type -%}
You are writing a {{metadata.doc_type}}.
{%- endif -%}
`
});
```
Use session templates when you need different behavior for the same agent in different contexts (e.g., customer service vs internal support).
## Jinja2 Features
System templates support the full range of Jinja2 features:
### Conditionals
```jinja theme={"dark"}
{%- if user.subscription_level == "premium" -%}
You have access to advanced features.
{%- elif user.subscription_level == "basic" -%}
Some features may be limited.
{%- else -%}
Welcome! Consider upgrading for more features.
{%- endif -%}
```
### Loops
```jinja theme={"dark"}
{%- if metadata.topics -%}
Focus on these topics:
{%- for topic in metadata.topics -%}
- {{topic}}
{%- endfor -%}
{%- endif -%}
```
### Filters
```jinja theme={"dark"}
Agent name in uppercase: {{agent.name|upper}}
Word count limit: {{metadata.word_limit|default(500)}}
```
### Comments
```jinja theme={"dark"}
{# This is a comment and won't appear in the output #}
```
## Best Practices
Avoid overly complex templates. If your template is becoming too large, consider breaking the logic into different agents or sessions.
When using metadata, choose clear, descriptive keys:
* ✅ `metadata.customer_tier`
* ❌ `metadata.ct`
Always handle cases where variables might be missing:
```jinja theme={"dark"}
{{metadata.language|default("English")}}
```
Test your templates with various metadata combinations to ensure they render correctly in all scenarios.
## Common Patterns
### Multi-language Support
```jinja theme={"dark"}
{%- if metadata.language == "es" -%}
Eres un asistente útil. Responde en español.
{%- elif metadata.language == "fr" -%}
Vous êtes un assistant utile. Répondez en français.
{%- else -%}
You are a helpful assistant. Respond in English.
{%- endif -%}
```
### Role-based Access
```jinja theme={"dark"}
{%- if metadata.user_role == "admin" -%}
You have full access to all operations and sensitive information.
{%- elif metadata.user_role == "user" -%}
Provide general assistance. Do not share sensitive information.
{%- endif -%}
```
### Context-aware Behavior
```jinja theme={"dark"}
{%- if metadata.context == "code_review" -%}
Focus on code quality, security issues, and best practices.
{%- elif metadata.context == "debugging" -%}
Help identify and fix issues. Ask clarifying questions.
{%- elif metadata.context == "learning" -%}
Explain concepts clearly with examples. Be patient and thorough.
{%- endif -%}
```
## Debugging Templates
To debug template rendering issues:
1. **Check Variable Availability**: Ensure all referenced variables exist
2. **Validate Jinja2 Syntax**: Use a Jinja2 linter or validator
3. **Test Incrementally**: Add template features one at a time
4. **Use the Render Endpoint**: Test template rendering without making chat requests
```python python theme={"dark"}
# Test template rendering
render_result = client.sessions.render(
session_id=session.id,
messages=[{"role": "user", "content": "Test"}],
metadata={"test_var": "test_value"}
)
# Inspect the rendered system message
print(render_result.messages[0]["content"])
```
```javascript javascript theme={"dark"}
// Test template rendering
const renderResult = await client.sessions.render({
sessionId: session.id,
messages: [{ role: "user", content: "Test" }],
metadata: { test_var: "test_value" }
});
// Inspect the rendered system message
console.log(renderResult.messages[0].content);
```
## Related Topics
* [Agents](/concepts/agents) - Learn more about agent configuration
* [Sessions](/concepts/sessions) - Understand session management
* [Tasks](/concepts/tasks) - Create complex workflows with templates
* [Tool Integration](/integrations/tools) - Add tools referenced in templates
System templates are rendered server-side before being sent to the language model. This ensures security and consistency across all API clients.
# Types of Task Steps
Source: https://docs.julep.ai/advanced/types-of-task-steps
Learn about different types of task steps and their use
## Overview
In Julep broadly speaking there are two types of steps:
These steps control the flow of the task. They are used to create conditional logic, loops, and parallel execution.
These steps are used to get and set values in the task.
These steps are used to iterate over a collection.
These steps are used to create conditional logic.
These steps are used to control the flow of the task.
The steps defined out here are in the YAML format. You can learn more about the YAML format [here](https://yaml.org/spec/1.2.2/).
## Control Flow Steps
### Prompt Step
Send messages to the AI model:
```yaml YAML theme={"dark"}
# Simple prompt
- prompt: What is your name?
# Multi-message prompt
- prompt:
- role: system
content: You are a helpful assistant
- role: user
content: "Hello!"
# Prompt with settings
- prompt:
- role: user
content: Generate a creative story
settings:
model: "claude-3.5-sonnet"
temperature: 0.8
# Prompt with automatic tool execution
- prompt: What's the weather in San Francisco?
auto_run_tools: true # Tools execute automatically if needed
# Prompt with JSON response format
- prompt:
- role: system
content: You are a helpful assistant that always responds in JSON format.
- role: user
content: List 3 interesting facts about space. Format as JSON with 'facts' array.
settings:
model: "gpt-4o-mini"
temperature: 0.7
response_format:
type: json_object
# Prompt with JSON schema for structured output
- prompt:
- role: system
content: You are a data extraction specialist.
- role: user
content: Extract product information from the description.
settings:
response_format:
type: json_schema
json_schema:
name: product_info
schema:
type: object
properties:
product_name:
type: string
features:
type: array
items:
type: string
price:
type: number
required: ["product_name", "features", "price"]
```
When `auto_run_tools` is set to `true` in a prompt step, any tools available to the agent will be automatically executed if the model decides to use them. The results are then fed back to the model to continue processing. This is particularly useful for creating autonomous workflows where the agent can gather information and make decisions without manual intervention.
The `response_format` setting allows you to request structured output from the model. There are two main options:
* `type: json_object` - Ensures the model responds with valid JSON
* `type: json_schema` - Enforces a specific JSON structure defined by a schema
When using `response_format`, make sure to instruct the model to produce JSON in your prompt (via system or user message) for best results. Response format support varies by model provider - check the [supported models documentation](/integrations/supported-models) for compatibility.
In the prompt step we offer a bunch of Python functions to help you manipulate data. Here is a list of the functions you can use:
* Standard library modules:
* `re`: Regular expressions (safe against ReDoS)
* `json`: JSON encoding/decoding
* `yaml`: YAML parsing/dumping
* `string`: String constants and operations
* `datetime`: Date and time operations
* `math`: Mathematical functions
* `statistics`: Statistical operations
* `base64`: Base64 encoding/decoding
* `urllib`: URL parsing operations
* `random`: Random number generation
* `time`: Time operations
* Constants:
* `NEWLINE`: Newline character
* `true`: Boolean true
* `false`: Boolean false
* `null`: None value
### Tool Call Step
Execute tools defined in the task:
```yaml YAML theme={"dark"}
# Simple tool call
- tool: web_search
arguments:
query: Latest AI news
# Tool call with complex arguments
- tool: process_data
arguments:
input_data: $ _.previous_result
options:
format: "json"
validate: true
```
### Evaluate Step
Perform calculations or data manipulation:
```yaml YAML theme={"dark"}
# Simple evaluation
- evaluate:
count: $ len(_.results)
# Multiple evaluations
- evaluate:
total: $ sum(_.numbers)
average: $ _.total / len(_.numbers)
formatted: $ f'Average: {_.average:.2f}'
```
In the evaluate step we offer a bunch of Python functions to help you manipulate data. Check out the [Python Expressions](/advanced/python-expression#available-functions-and-libraries) for more information.
### Wait for Input Step
Pause workflow for user input:
```yaml YAML theme={"dark"}
# Simple input request
- wait_for_input:
info:
message: "Please provide your name"
# Input with validation
- wait_for_input:
info:
message: "Enter your age"
validation:
type: "number"
minimum: 0
maximum: 150
```
### Subworkflow Step
Executing a subworkflow from a main workflow:
```yaml YAML [expandable] theme={"dark"}
# Subworkflow
subworkflow:
- evaluate:
main_workflow_input: $ _.content # you can use steps[0].input.content to access the input of the subworkflow
- return:
result: "This is the subworkflow"
# Main workflow
main:
# Step 0: Evaluate step
- evaluate:
result: "This is the main workflow"
# Step 1: Call the subworkflow
- workflow: subworkflow # name of the subworkflow
arguments: # input to the subworkflow
content: $ _.result # you can use steps[0].output.result to access the result of the previous step
# Step 2: Evaluate step
- evaluate:
subworkflow_result: $ steps[1].output.result # this will be the result of the subworkflow
```
* The `arguments` passed from the main workflow to the subworkflow are available in the `steps[0].input` of the subworkflow.
* The `result` of the subworkflow is available in the `steps[1].output.result` of the main workflow.
* The `Input/Output Data References` between steps is exclusive to the workflow they are defined in.
A workflow cannot reference the `input` or `output` of another workflow between the steps. To learn more about `Input/Output Data References` [click here](/advanced/new-syntax#1-input-output-data-references).
Self recursion is allowed in a subworkflow but not in a main workflow.
## Key-Value Steps
### Get Step
Retrieve values from storage:
```yaml YAML theme={"dark"}
# Get a single value
- get: user_preference
# Get multiple values
- get:
- preference1
- preference2
```
### Set Step
Store values for later use:
```yaml YAML theme={"dark"}
# Set a single value
- set:
user_name: John
# Set multiple values
- set:
count: $ len(_.results)
has_data: $ _.count > 0
```
Values stored using the set step are added to the workflow's global `state` object, which can be accessed anywhere in the workflow using `state.variable_name`. For example:
```yaml YAML theme={"dark"}
# Access previously set values
- evaluate:
greeting: $ f"Hello, {state.user_name}!"
data_status: $ f"Has data: {state.has_data}, Count: {state.count}"
```
Each subworkflow has its own isolated `state` object. Values set in one subworkflow are not accessible from other subworkflows or the parent workflow.
### Label Step
Label a step to make it easier to identify and access those values later in any step:
```yaml YAML theme={"dark"}
# Step 0: Set a single value
- set:
user_name: John
label: get_user_name
# Step 1: Set multiple values
- set:
count: $ len(_.results)
has_data: $ _.count > 0
label: get_count_and_has_data
```
In any steps following the label step, you can access the values set in the label step using the `$ steps['label_name'].input.attribute_name` or `$ steps['label_name'].output.attribute_name` syntax. For example:
```yaml YAML theme={"dark"}
- evaluate:
user_name: $ steps['get_user_name'].output.user_name
- evaluate:
count: $ steps['get_count_and_has_data'].output.count
has_data: $ steps['get_count_and_has_data'].output.has_data
```
## Iteration Steps
### Foreach Step
Iterate over a collection:
```yaml YAML theme={"dark"}
# Simple foreach
- foreach:
in: $ _.items
do:
log: $ f'Processing {_}'
# Foreach with complex processing
- foreach:
in: $ _.documents
do:
tool: analyze
arguments:
text: $ _.content
evaluate:
results: $ _ + [_.analysis]
```
### Map-Reduce Step
Process collections in parallel:
```yaml YAML theme={"dark"}
# Simple map-reduce
- over: $ _.urls
map:
tool: fetch_content
arguments:
url: $ _
reduce: $ results + [_]
# Map-reduce with parallelism
- over: $ _.queries
map:
tool: web_search
arguments:
query: $ _
parallelism: 5 # Number of parallel steps to execute
```
* By default the `parallelism` if not mentioned is 100. If mentioned, it is the maximum number of steps that can run in parallel concurrently.
* When using `over` step, the `map` step is executed for each value in the collection.
* The `reduce` step is executed after the `map` step.
## Conditional Steps
### If-Else Step
Conditional execution:
```yaml YAML [expandable] theme={"dark"}
# Simple if
- if: $ _.count > 0
then:
log: Found results
# If-else
- if: $ _.score > 0.8
then:
log: High score
else:
log: Low score
# If-else with multiple conditions
- if: $ 1 > 0
then:
if: $ 2 > 1
then:
evaluate:
x: y
else:
evaluate:
x: z
```
### Switch Step
Multiple condition handling:
```yaml YAML theme={"dark"}
# Switch statement
- switch:
- case: $ _.category == "A"
then:
- log: Category A
- case: $ _.category == "B"
then:
- log: Category B
- case: $ _ # Default case
then:
- log: Unknown category
```
## Other Control Flow
### Sleep Step
Pause execution:
```yaml YAML theme={"dark"}
# Sleep for duration
- sleep:
seconds: 30
# Sleep with different units
- sleep:
minutes: 5
# hours: 1
# days: 1
```
### Return Step
Return values from workflow:
```yaml YAML theme={"dark"}
# Simple return
- return: $ _.result
# Structured return
- return:
data: $ _.processed_data
metadata:
count: $ _.count
timestamp: $ datetime.now().isoformat()
```
### Log Step
Log messages or specific values:
```yaml YAML theme={"dark"}
- log: $ f'Processing completed for item {item_id}'
```
### Error Step
Handle errors by specifying an error message:
```yaml YAML theme={"dark"}
- error: Invalid input provided
```
## Example: Complex Workflow
Here's an example combining various step types:
```yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Multi-Step Task Demonstration
description: A demonstration of multi-step task processing with research and summarization capabilities.
################################################################################
############################# INPUT SCHEMA #####################################
################################################################################
input_schema:
type: object
properties:
topic:
type: string
description: The topic to research and summarize.
################################################################################
############################# TOOLS ############################################
################################################################################
# Describing the tools that will be used in the workflow
tools:
- name: web_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
################################################################################
############################# MAIN WORKFLOW ####################################
################################################################################
main:
# Step 0: Generate initial research questions
- prompt:
- role: system
content: >-
$ f'''
You are a research assistant. Your task is to formulate three specific research questions about the given topic: {steps[0].input.topic}'''
unwrap: true
# Step 1: Web search for each question
- foreach:
in: $ _.split('\\n')
do:
tool: web_search
arguments:
query: $ _
# Step 2: Extract relevant information
- evaluate:
relevant_info: $ [output for output in _]
# Step 3: Process and summarize information
- if: $ len(_.relevant_info) >= 3
then:
prompt:
- role: system
content: >-
$ f'''
Summarize the following information about {steps[0].input.topic}:
{_.relevant_info}'''
unwrap: true
else:
prompt:
- role: system
content: >-
$ f'''
Not enough information gathered. Please provide a brief overview of {steps[0].input.topic} based on your knowledge.'''
unwrap: true
# Step 4: Record the summary
- log: >-
$ f'''
Summary for {steps[0].input.topic}: {_}'''
# Step 5: Prepare final output
- return:
summary: $ _
topic: $ steps[0].input.topic
```
## Best Practices
- Group related steps logically
- Use comments to explain complex steps
- Keep step chains focused and manageable
- Use if-else for error conditions
- Provide fallback options
- Log important state changes
- Use parallel execution when possible
- Optimize data passing between steps
- Cache frequently used values
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Create Agent
Source: https://docs.julep.ai/api-reference/agents/create-agent
https://api.julep.ai/api/openapi.json post /agents
# Create Agent Tool
Source: https://docs.julep.ai/api-reference/agents/create-agent-tool
https://api.julep.ai/api/openapi.json post /agents/{agent_id}/tools
# Create Or Update Agent
Source: https://docs.julep.ai/api-reference/agents/create-or-update-agent
https://api.julep.ai/api/openapi.json post /agents/{agent_id}
# Delete Agent
Source: https://docs.julep.ai/api-reference/agents/delete-agent
https://api.julep.ai/api/openapi.json delete /agents/{agent_id}
# Delete Agent Tool
Source: https://docs.julep.ai/api-reference/agents/delete-agent-tool
https://api.julep.ai/api/openapi.json delete /agents/{agent_id}/tools/{tool_id}
# Get Agent Details
Source: https://docs.julep.ai/api-reference/agents/get-agent-details
https://api.julep.ai/api/openapi.json get /agents/{agent_id}
# List Agent Tools
Source: https://docs.julep.ai/api-reference/agents/list-agent-tools
https://api.julep.ai/api/openapi.json get /agents/{agent_id}/tools
# List Agents
Source: https://docs.julep.ai/api-reference/agents/list-agents
https://api.julep.ai/api/openapi.json get /agents
# List Models
Source: https://docs.julep.ai/api-reference/agents/list-models
https://api.julep.ai/api/openapi.json get /agents/models
List all available models that can be used with agents.
Returns:
ListModelsResponse: A list of available models
# Patch Agent
Source: https://docs.julep.ai/api-reference/agents/patch-agent
https://api.julep.ai/api/openapi.json patch /agents/{agent_id}
# Patch Agent Tool
Source: https://docs.julep.ai/api-reference/agents/patch-agent-tool
https://api.julep.ai/api/openapi.json patch /agents/{agent_id}/tools/{tool_id}
# Update Agent
Source: https://docs.julep.ai/api-reference/agents/update-agent
https://api.julep.ai/api/openapi.json put /agents/{agent_id}
# Update Agent Tool
Source: https://docs.julep.ai/api-reference/agents/update-agent-tool
https://api.julep.ai/api/openapi.json put /agents/{agent_id}/tools/{tool_id}
# Bulk Delete Agent Docs
Source: https://docs.julep.ai/api-reference/docs/bulk-delete-agent-docs
https://api.julep.ai/api/openapi.json delete /agents/{agent_id}/docs
Bulk delete documents owned by an agent based on metadata filter
# Bulk Delete User Docs
Source: https://docs.julep.ai/api-reference/docs/bulk-delete-user-docs
https://api.julep.ai/api/openapi.json delete /users/{user_id}/docs
Bulk delete documents owned by a user based on metadata filter
# Create Agent Doc
Source: https://docs.julep.ai/api-reference/docs/create-agent-doc
https://api.julep.ai/api/openapi.json post /agents/{agent_id}/docs
# Create User Doc
Source: https://docs.julep.ai/api-reference/docs/create-user-doc
https://api.julep.ai/api/openapi.json post /users/{user_id}/docs
Creates a new document for a user.
Parameters:
user_id (UUID): The unique identifier of the user associated with the document.
data (CreateDocRequest): The data to create the document with.
x_developer_id (UUID): The unique identifier of the developer associated with the document.
Returns:
Doc: The created document.
# Delete Agent Doc
Source: https://docs.julep.ai/api-reference/docs/delete-agent-doc
https://api.julep.ai/api/openapi.json delete /agents/{agent_id}/docs/{doc_id}
# Delete User Doc
Source: https://docs.julep.ai/api-reference/docs/delete-user-doc
https://api.julep.ai/api/openapi.json delete /users/{user_id}/docs/{doc_id}
# Embed
Source: https://docs.julep.ai/api-reference/docs/embed
https://api.julep.ai/api/openapi.json post /embed
# Get Doc
Source: https://docs.julep.ai/api-reference/docs/get-doc
https://api.julep.ai/api/openapi.json get /docs/{doc_id}
# List Agent Docs
Source: https://docs.julep.ai/api-reference/docs/list-agent-docs
https://api.julep.ai/api/openapi.json get /agents/{agent_id}/docs
# List User Docs
Source: https://docs.julep.ai/api-reference/docs/list-user-docs
https://api.julep.ai/api/openapi.json get /users/{user_id}/docs
# Search Agent Docs
Source: https://docs.julep.ai/api-reference/docs/search-agent-docs
https://api.julep.ai/api/openapi.json post /agents/{agent_id}/search
Searches for documents associated with a specific agent.
Parameters:
x_developer_id (UUID): The unique identifier of the developer associated with the agent.
search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search.
agent_id (UUID): The umnique identifier of the agent associated with the documents.
Returns:
DocSearchResponse: The search results.
# Search User Docs
Source: https://docs.julep.ai/api-reference/docs/search-user-docs
https://api.julep.ai/api/openapi.json post /users/{user_id}/search
Searches for documents associated with a specific user.
Parameters:
x_developer_id (UUID): The unique identifier of the developer associated with the user.
search_params (TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest): The parameters for the search.
user_id (UUID): The unique identifier of the user associated with the documents.
Returns:
DocSearchResponse: The search results.
# Create Task Execution
Source: https://docs.julep.ai/api-reference/executions/create-task-execution
https://api.julep.ai/api/openapi.json post /tasks/{task_id}/executions
# Get Execution Details
Source: https://docs.julep.ai/api-reference/executions/get-execution-details
https://api.julep.ai/api/openapi.json get /executions/{execution_id}
# List Execution Transitions
Source: https://docs.julep.ai/api-reference/executions/list-execution-transitions
https://api.julep.ai/api/openapi.json get /executions/{execution_id}/transitions
# Stream Execution Status
Source: https://docs.julep.ai/api-reference/executions/stream-execution-status
https://api.julep.ai/api/openapi.json get /executions/{execution_id}/status.stream
SSE endpoint that streams the status of a given execution_id by polling the
latest_executions view.
# Stream Transitions Events
Source: https://docs.julep.ai/api-reference/executions/stream-transitions-events
https://api.julep.ai/api/openapi.json get /executions/{execution_id}/transitions.stream
# Update Execution
Source: https://docs.julep.ai/api-reference/executions/update-execution
https://api.julep.ai/api/openapi.json put /executions/{execution_id}
# Create File
Source: https://docs.julep.ai/api-reference/files/create-file
https://api.julep.ai/api/openapi.json post /files
# Delete File
Source: https://docs.julep.ai/api-reference/files/delete-file
https://api.julep.ai/api/openapi.json delete /files/{file_id}
# Get File
Source: https://docs.julep.ai/api-reference/files/get-file
https://api.julep.ai/api/openapi.json get /files/{file_id}
# List Files
Source: https://docs.julep.ai/api-reference/files/list-files
https://api.julep.ai/api/openapi.json get /files
# Check Health
Source: https://docs.julep.ai/api-reference/healthz/check-health
https://api.julep.ai/api/openapi.json get /healthz
# Get Job Status
Source: https://docs.julep.ai/api-reference/jobs/get-job-status
https://api.julep.ai/api/openapi.json get /jobs/{job_id}
# Create Project
Source: https://docs.julep.ai/api-reference/projects/create-project
https://api.julep.ai/api/openapi.json post /projects
# List Projects
Source: https://docs.julep.ai/api-reference/projects/list-projects
https://api.julep.ai/api/openapi.json get /projects
# Create Developer Secret
Source: https://docs.julep.ai/api-reference/secrets/create-developer-secret
https://api.julep.ai/api/openapi.json post /secrets
Create a new secret for a developer.
Args:
developer_id: ID of the developer creating the secret
secret: Secret to create
Returns:
The created secret
Raises:
HTTPException: If a secret with this name already exists (409 Conflict)
# Delete Developer Secret
Source: https://docs.julep.ai/api-reference/secrets/delete-developer-secret
https://api.julep.ai/api/openapi.json delete /secrets/{secret_id}
Delete a secret.
Args:
secret_id: ID of the secret to delete
x_developer_id: ID of the developer who owns the secret
Returns:
The deleted secret
Raises:
HTTPException: If the secret doesn't exist
# List Developer Secrets
Source: https://docs.julep.ai/api-reference/secrets/list-developer-secrets
https://api.julep.ai/api/openapi.json get /secrets
List all secrets for a developer.
Args:
x_developer_id: ID of the developer whose secrets to list
limit: Maximum number of secrets to return
offset: Number of secrets to skip
Returns:
List of secrets
# Update Developer Secret
Source: https://docs.julep.ai/api-reference/secrets/update-developer-secret
https://api.julep.ai/api/openapi.json put /secrets/{secret_id}
Update a developer secret.
Args:
developer_id: ID of the developer who owns the secret
secret_id: ID of the secret to update
data: New secret data
Returns:
The updated secret
Raises:
HTTPException: If the secret doesn't exist or doesn't belong to the developer
# Chat
Source: https://docs.julep.ai/api-reference/sessions/chat
https://api.julep.ai/api/openapi.json post /sessions/{session_id}/chat
Initiates a chat session.
Routes to different implementations based on feature flags:
- If auto_run_tools_chat feature flag is enabled, uses the new auto-tools implementation
- Otherwise, uses the legacy implementation
Parameters:
developer (Developer): The developer associated with the chat session.
session_id (UUID): The unique identifier of the chat session.
chat_input (ChatInput): The chat input data.
background_tasks (BackgroundTasks): The background tasks to run.
x_custom_api_key (Optional[str]): The custom API key.
mock_response (Optional[str]): Mock response for testing.
connection_pool: Connection pool for testing purposes.
Returns:
ChatResponse or StreamingResponse: The chat response or streaming response.
# Create Or Update Session
Source: https://docs.julep.ai/api-reference/sessions/create-or-update-session
https://api.julep.ai/api/openapi.json post /sessions/{session_id}
# Create Session
Source: https://docs.julep.ai/api-reference/sessions/create-session
https://api.julep.ai/api/openapi.json post /sessions
# Delete Session
Source: https://docs.julep.ai/api-reference/sessions/delete-session
https://api.julep.ai/api/openapi.json delete /sessions/{session_id}
# Get Session
Source: https://docs.julep.ai/api-reference/sessions/get-session
https://api.julep.ai/api/openapi.json get /sessions/{session_id}
# Get Session History
Source: https://docs.julep.ai/api-reference/sessions/get-session-history
https://api.julep.ai/api/openapi.json get /sessions/{session_id}/history
# List Sessions
Source: https://docs.julep.ai/api-reference/sessions/list-sessions
https://api.julep.ai/api/openapi.json get /sessions
# Patch Session
Source: https://docs.julep.ai/api-reference/sessions/patch-session
https://api.julep.ai/api/openapi.json patch /sessions/{session_id}
# Render
Source: https://docs.julep.ai/api-reference/sessions/render
https://api.julep.ai/api/openapi.json post /sessions/{session_id}/render
Renders a chat input.
Routes to different implementations based on feature flags:
- If auto_run_tools_chat feature flag is enabled, uses the new auto-tools implementation
- Otherwise, uses the legacy implementation
Parameters:
developer (Developer): The developer associated with the chat session.
session_id (UUID): The unique identifier of the chat session.
chat_input (ChatInput): The chat input data.
Returns:
RenderResponse: The rendered chat input.
# Update Session
Source: https://docs.julep.ai/api-reference/sessions/update-session
https://api.julep.ai/api/openapi.json put /sessions/{session_id}
# Create Or Update Task
Source: https://docs.julep.ai/api-reference/tasks/create-or-update-task
https://api.julep.ai/api/openapi.json post /agents/{agent_id}/tasks/{task_id}
# Create Task
Source: https://docs.julep.ai/api-reference/tasks/create-task
https://api.julep.ai/api/openapi.json post /agents/{agent_id}/tasks
# Get Execution Transition
Source: https://docs.julep.ai/api-reference/tasks/get-execution-transition
https://api.julep.ai/api/openapi.json get /executions/{execution_id}/transitions/{transition_id}
# Get Task Details
Source: https://docs.julep.ai/api-reference/tasks/get-task-details
https://api.julep.ai/api/openapi.json get /tasks/{task_id}
# List Task Executions
Source: https://docs.julep.ai/api-reference/tasks/list-task-executions
https://api.julep.ai/api/openapi.json get /tasks/{task_id}/executions
# List Tasks
Source: https://docs.julep.ai/api-reference/tasks/list-tasks
https://api.julep.ai/api/openapi.json get /agents/{agent_id}/tasks
# Decode Payloads
Source: https://docs.julep.ai/api-reference/temporal/decode-payloads
https://api.julep.ai/api/openapi.json post /temporal/decode
Decodes a list of payloads from the request body.
Args:
req (Request): The request containing the payloads to decode.
Returns:
dict: A dictionary containing the decoded payloads, including any errors encountered.
# Create Or Update User
Source: https://docs.julep.ai/api-reference/users/create-or-update-user
https://api.julep.ai/api/openapi.json post /users/{user_id}
# Create User
Source: https://docs.julep.ai/api-reference/users/create-user
https://api.julep.ai/api/openapi.json post /users
# Delete User
Source: https://docs.julep.ai/api-reference/users/delete-user
https://api.julep.ai/api/openapi.json delete /users/{user_id}
# Get User Details
Source: https://docs.julep.ai/api-reference/users/get-user-details
https://api.julep.ai/api/openapi.json get /users/{user_id}
# List Users
Source: https://docs.julep.ai/api-reference/users/list-users
https://api.julep.ai/api/openapi.json get /users
# Patch User
Source: https://docs.julep.ai/api-reference/users/patch-user
https://api.julep.ai/api/openapi.json patch /users/{user_id}
# Update User
Source: https://docs.julep.ai/api-reference/users/update-user
https://api.julep.ai/api/openapi.json put /users/{user_id}
# Agents
Source: https://docs.julep.ai/concepts/agents
Understanding Julep Agents and their capabilities
## Overview
Agents are conceptual entities that encapsulate all the configurations and settings of an LLM, enabling it to adopt unique personas and execute distinct tasks within an application.
## Components
Agents are made up of several components. Think of components as the building blocks of an agent required to perform a task. Here are the key components associated with an agent:
* Instructions - Agent configuration that can be provided as either a single string or an array of strings.
* Metadata - Key-value pair that can be used to categorize and filter agents.
* Tools - Functions that can be used by an agent to perform tasks. Julep supports a wide range of tools, including custom tools, which are functions that can be used by an agent to perform tasks.
* Docs - A collection of documents that can be used by an agent to retrieve information. Docs can be associated with an agent and can be used to retrieve or search information from the agent's context.
### Agent Configuration Options
When creating an agent, you can leverage the following configuration options:
| Option | Type | Description | Default |
| ------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| `name` | `string` | The name of your agent | Required |
| `canonical_name` | `string` | A unique identifier for your agent, following the pattern `[a-zA-Z][a-zA-Z0-9_]*` | `null` |
| `project` | `string` | The canonical name of the project this agent belongs to | `"default"` |
| `about` | `string` | A brief description of what your agent does | `""` |
| `model` | `string` | The language model your agent uses (e.g., "gpt-4-turbo", "gemini-nano") | `""` |
| `instructions` | `string` \| `list[string]` | Specific tasks or behaviors expected from the agent | `[]` |
| `metadata` | `object` | Key-value pairs for additional information about your agent | `null` |
| `default_settings` | `object` | Default configuration settings for the agent. See [supported parameters](/integrations/supported-models#supported-parameters) for details. | `null` |
| `default_system_template` | `string` | Default system template for all sessions created by this agent. | See [default system template](/concepts/agents#default-system-template) |
The **System Template** is a specific system prompt written as a Jinja template that sets the foundational context and instructions for the agent within a session. It defines the background, directives, and any relevant information that the agent should consider when interacting with the user.
For a comprehensive guide on system templates including available variables, customization options, and advanced usage patterns, see the [System Templates](/advanced/system-templates) documentation. For more details on Jinja templates, refer to the [Jinja documentation](https://jinja.palletsprojects.com/).
```python Python [expandable] theme={"dark"}
{%- if agent.name -%}
You are {{ agent.name }}.
{%- endif -%}
{%- if agent.about -%}
About you: {{ agent.about }}.
{%- 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 -%}
{{ NEWLINE }}
{%- if session.situation -%}
Situation: {{ session.situation }}
{%- endif -%}
{{ NEWLINE + NEWLINE }}
{%- if agent.instructions -%}
Instructions:
{%- if agent.instructions is string -%}
{{ agent.instructions }}
{%- else -%}
{%- for instruction in agent.instructions -%}
- {{ instruction }}
{%- endfor -%}
{%- endif -%}
{{ NEWLINE }}
{%- endif -%}
{%- if docs -%}
Relevant documents:
{%- for doc in docs -%}
{{ doc.title }}
{%- if doc.content is string -%}
{{ doc.content }}
{%- else -%}
{%- for snippet in doc.content -%}
{{ snippet }}
{%- endfor -%}
{%- endif -%}
---
{%- endfor -%}
{%- endif -%}
```
## How to Use Agents
In Julep, how you use agents is very important. The YAML below shows the anatomy of an agent.
```yaml YAML theme={"dark"}
name: "My Agent"
model: "claude-3.5-sonnet"
project: "data-analytics"
about: "A helpful AI assistant that specializes in data analysis"
instructions: "You are a helpful AI assistant that specializes in data analysis"
metadata:
type: "data-analysis"
tools:
- name: "calculate_total"
description: "Calculate the total of a list of numbers"
function:
parameters:
type: "object"
properties:
numbers:
type: "array"
items:
type: "number"
```
### Creating an Agent
To create an agent, you can use the `create` method in the Python or Node.js SDK.
```python Python theme={"dark"}
from julep import Julep
client = Julep(api_key="your_api_key")
agent = client.agents.create(
name="My Agent",
model="claude-3.5-sonnet",
about="A helpful AI assistant that specializes in data analysis",
instructions="You are a helpful AI assistant that specializes in data analysis",
metadata={"type": "data-analysis"},
tools=[
{
"name": "calculate_total",
"description": "Calculate the total of a list of numbers",
"function": {
"parameters": {"type": "object", "properties": {"numbers": {"type": "array", "items": {"type": "number"}}}}
}
}
]
)
```
```javascript Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
const client = new Julep({ apiKey: 'your_api_key' });
const agent = await client.agents.create({
name: "My Agent",
model: "claude-3.5-sonnet",
about: "A helpful AI assistant that specializes in data analysis",
instructions: "You are a helpful AI assistant that specializes in data analysis",
metadata: {"type": "data-analysis"},
tools: [
{
"name": "calculate_total",
"description": "Calculate the total of a list of numbers",
"function": {
"parameters": {"type": "object", "properties": {"numbers": {"type": "array", "items": {"type": "number"}}}}
}
}
]
});
```
### Using default\_settings with response\_format
You can configure your agent to always return structured JSON responses by setting `response_format` in the `default_settings`:
```python Python theme={"dark"}
agent = client.agents.create(
name="Data Analyst",
model="gpt-4o-mini",
about="An agent that analyzes data and returns structured insights",
instructions="Always provide analysis results in a structured format",
default_settings={
"temperature": 0.3,
"response_format": {
"type": "json_object"
}
}
)
```
```javascript Node.js theme={"dark"}
const agent = await client.agents.create({
name: "Data Analyst",
model: "gpt-4o-mini",
about: "An agent that analyzes data and returns structured insights",
instructions: "Always provide analysis results in a structured format",
default_settings: {
temperature: 0.3,
response_format: {
type: "json_object"
}
}
});
```
Check out the API reference [here](/api-reference/agents/create-agent) or SDK reference (Python [here](/sdks/python/reference#agents) or JavaScript [here](/sdks/nodejs/reference#agents) for more details on different operations you can perform on agents.
## Relationship To Other Concepts
This section will help you understand how agents relate to other concepts in Julep.
### Projects
Agents belong to exactly one project, which helps organize related resources together. When creating an agent, you can specify which project it belongs to using the `project` parameter. If not specified, the agent will be assigned to the "default" project.
For example:
```python Python theme={"dark"}
# Create an agent in a specific project
agent = client.agents.create(
name="Customer Support Bot",
project="support-platform"
)
```
```javascript Node.js theme={"dark"}
// Create an agent in a specific project
const agent = await client.agents.create({
name: "Customer Support Bot",
project: "support-platform"
});
```
For more information about projects, see [Projects](/concepts/projects).
### Tools
Agents can be associated with different types of tools available in Julep to enable them to perform operations. These tools associated with an agent can also be leveraged by a task associated with the agent.
For example:
```python Python theme={"dark"}
# Create an agent
agent = client.agents.create(name="My Agent")
# Associate a tool with the agent
client.agents.tools.create(
agent_id=AGENT_UUID,
**{
"name": "computer",
"type": "computer_20241022",
"computer_20241022": {
"display_height_px": 768,
"display_width_px": 1024,
"display_number": 1,
},
}
)
```
```javascript Node.js theme={"dark"}
// Create an agent
const agent = await client.agents.create({name: "My Agent"});
// Associate a tool with the agent
await client.agents.tools.create(agent.id, {
name: "computer",
type: "computer_20241022",
computer_20241022: {
display_height_px: 768,
display_width_px: 1024,
display_number: 1,
},
});
```
### Sessions
Agents can be used in sessions to enable real-time, interactive conversations. While tasks are designed for automated workflows, sessions provide a way to have stateful, continuous interactions with an agent. You can create multiple sessions with the same agent or multiple agents in a session, each session maintaining its own conversation history and context. This makes sessions ideal for scenarios requiring ongoing dialogue or human-in-the-loop interactions.
For example:
```python Python theme={"dark"}
# Create an agent
agent = client.agents.create(name="My Agent")
# Create a session with the agent
session = client.sessions.create(agent=agent.id)
```
```javascript Node.js theme={"dark"}
// Create an agent
const agent = await client.agents.create({name: "My Agent"});
// Create a session with the agent
const session = await client.sessions.create({agent: agent.id});
```
## Best Practices
- **1. Clear, Focused Purposes**: Give agents clear, focused purposes rather than making them generalists
- **2. Descriptive Names**: Use descriptive names that reflect the agent's primary function
- **3. Concise Instructions**: Keep instructions concise but specific
- **4. Specialized Agents**: Break complex tasks into multiple specialized agents rather than one complex agent
- **1. Conservative Model Settings**: Start with conservative model settings (temperature, top\_p) and adjust as needed
- **2. Metadata**: Use metadata effectively for organization and filtering
- **3. Tools**: Define tools that are specific to the agent's purpose
- **1. Reuse Agents**: Reuse agents across similar tasks instead of creating new ones
- **2. Clean Up**: Clean up unused agents and their associated resources
- **3. Monitor Token Usage**: Monitor token usage and adjust context windows appropriately
Avoid giving agents more capabilities than they need. Each additional tool or permission increases the complexity and potential security surface area.
## Next Steps
* [Agent Tools](/concepts/tools) - Learn about tools and how to use them with agents
* [Agent Tasks](/concepts/tasks) - Learn about tasks and how to use them with agents
* [Agent Sessions](/concepts/sessions) - Learn about sessions and how to use them with agents
* [Agent Docs](/concepts/docs) - Learn about docs and how to use them with agents
## See Examples
* [Hello Agent notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/basics/01-Hello-Agent.ipynb)
* [Companion Agent notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/09-companion-agent.ipynb)
# Documents (RAG)
Source: https://docs.julep.ai/concepts/docs
Working with documents in Julep
## Overview
Documents in Julep provide a way to store and retrieve information that can be used by agents. This section covers how to work with documents effectively.
## Components
Documents in Julep consist of several key components that enable efficient storage and retrieval of information.
* **Title**: The title component helps identify and organize documents.
* **Content**: The textual content of the document.
* **Embeddings** (automatically generated): The vector representations of text that enable semantic search capabilities. Generated using the `text-embedding-3-large` model from OpenAI.
* **Metadata**: Metadata provides additional context and filtering capabilities for documents.
### Docs Configuration Options
When creating a doc, the following attributes can be specified:
| **Field** | **Type** | **Description** | **Default** |
| ---------- | -------- | ---------------------------------------------------------------------- | ----------- |
| `title` | `string` | The title of the document. | Required |
| `content` | `string` | The content of the document. | Required |
| `metadata` | `object` | Additional metadata for the document, such as preferences or settings. | `null` |
## How to Use Docs
### Creating a Doc
Documents are attached to either an agent or a user. This is how you can create a doc using Julep's SDKs.
**Example:**
```python Python theme={"dark"}
# Creating a doc
client.agents.docs.create(
agent_id=agent.id,
title="Old World",
content="The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa. In Europe, the era was marked by the rise of feudalism, the power of the Catholic Church, and the cultural blossoming of the Renaissance towards its end. Asia witnessed the flourishing of the Silk Road, facilitating trade and cultural exchange, and the rise of powerful empires such as the Mongol Empire, which at its height, stretched from Europe to Asia. Meanwhile, Africa saw the growth of influential kingdoms and empires like Mali, known for its wealth and the legendary pilgrimage of Mansa Musa, and the spread of Islam across the continent, which played a crucial role in shaping its cultural and social landscapes.",
metadata={"source": "https://en.wikipedia.org/wiki/Medieval_period"},
)
```
```javascript Node.js theme={"dark"}
// Creating a doc
const doc = await client.agents.docs.create({
agent_id: "agent_id",
title: "Old World",
content: "The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa. In Europe, the era was marked by the rise of feudalism, the power of the Catholic Church, and the cultural blossoming of the Renaissance towards its end. Asia witnessed the flourishing of the Silk Road, facilitating trade and cultural exchange, and the rise of powerful empires such as the Mongol Empire, which at its height, stretched from Europe to Asia. Meanwhile, Africa saw the growth of influential kingdoms and empires like Mali, known for its wealth and the legendary pilgrimage of Mansa Musa, and the spread of Islam across the continent, which played a crucial role in shaping its cultural and social landscapes.",
metadata: {"source": "https://en.wikipedia.org/wiki/Medieval_period"},
});
```
To create a user doc, replace `client.agents.docs.create` with `client.users.docs.create`, and the `agent_id` argument with `user_id`.
Check out the [API reference](/api-reference/create-user-doc) or SDK reference ([Python](/sdks/python/reference#docs) or [JavaScript](/sdks/nodejs/reference#docs)) for more details on different operations you can perform on docs.
### Getting a Doc
To get a doc, you can use the `client.docs.get` method, and pass the doc's ID.
**Example:**
```python Python theme={"dark"}
# Getting a doc
doc = client.docs.get(doc_id="doc_id")
print(doc)
```
```javascript Node.js theme={"dark"}
// Getting a doc
const doc = await client.docs.get("doc_id");
console.log(doc)
```
When you get a doc, you can access the doc's `title`, `content`, `embeddings`, `metadata` and other attributes.
```[expandable] theme={"dark"}
Doc(
id='0680b756-9827-7cf2-8000-82ed5daf816e',
content=['The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa.'],
created_at=datetime.datetime(2023, 4, 25, 11, 43, 37, 513161, tzinfo=datetime.timezone.utc),
title='Old World',
embedding_dimensions=1024,
embedding_model='text-embedding-3-large',
embeddings=[
-0.036940154,
0.021077264,
-0.03274468,
0.016222501,
...
-0.05038565,
0.012776218,
...
],
language='english',
metadata={"source": "https://en.wikipedia.org/wiki/Medieval_period"},
modality='text'
)
```
### Chunking
In Julep, documents are not automatically chunked. We recommend that developers handle chunking based on their specific use case requirements, as different applications may have unique needs for how documents should be divided.
For those who need assistance with chunking, we provide a utility function `chunk_doc` that can be used directly in task steps. For implementation details, you can check the source code for this method in [this file](https://github.com/julep-ai/julep/blob/main/agents-api/agents_api/activities/utils.py).
### Search
#### Full Text Search
Our full-text search functionality leverages Timescale's powerful indexing to efficiently match user-provided keywords and phrases against document content, delivering relevant results even with variations in text. Key features include prefix and infix searching, morphology processing (stemming and lemmatization), fuzzy searching to handle typos, and exact result counts.
**Parameters:**
| Parameter | Type | Description | Default |
| ------------------------------ | -------- | ------------------------------------------------------------------------------------ | ------------ |
| `text` | `str` | The textual query to search within documents. | **Required** |
| `metadata_filter` | `object` | Filters to apply based on document metadata. | `None` |
| `lang` | `str` | The language to use for full-text search processing. | `'english'` |
| `limit` | `int` | The maximum number of documents to return. | `10` |
| `trigram_similarity_threshold` | `float` | The threshold for trigram similarity matching (higher values require closer matches) | `0.6` |
The default parameters for full-text search are based on our internal benchmarking. These values provide a good starting point, but you may need to adjust them depending on your specific use case to achieve optimal results.
**Example:**
```python Python theme={"dark"}
# Define the query
text_query = "Medieval times in Europe"
# Search for docs
matched_docs = client.agents.docs.search(
agent_id="agent_id",
text=text_query,
limit=10, # the maximum number of docs to return
)
print(matched_docs.model_dump())
```
```javascript Node.js theme={"dark"}
// Define the query
const text_query = "Medieval times in Europe";
// Search for docs
const matched_docs = await client.agents.docs.search({
agent_id: "agent_id",
text: text_query,
limit: 10, // the maximum number of docs to return
});
console.log(matched_docs);
```
```
{'docs': [{'id': '06791e86-6dcf-7816-8000-e4c3b781d05b',
'owner': {'id': '06791e82-46b7-739c-8000-3428f9d4e40f', 'role': 'agent'},
'snippet': {'content': 'The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa. In Europe, the era was marked by the rise of feudalism, the power of the Catholic Church, and the cultural blossoming of the Renaissance towards its end. Asia witnessed the flourishing of the Silk Road, facilitating trade and cultural exchange, and the rise of powerful empires such as the Mongol Empire, which at its height, stretched from Europe to Asia. Meanwhile, Africa saw the growth of influential kingdoms and empires like Mali, known for its wealth and the legendary pilgrimage of Mansa Musa, and the spread of Islam across the continent, which played a crucial role in shaping its cultural and social landscapes.',
'index': 0,
'embedding': [
-0.036940154,
0.021077264,
-0.03274468,
0.016222501,
-0.05038565,
0.012776218,
...
...]},
'distance': 0.6666666865348816,
'metadata': {"source": "https://en.wikipedia.org/wiki/Medieval_period"},
'title': 'Old World'}],
'time': 0.0522150993347168}
```
Check out the [API reference](/api-reference/search-agent-docs) or SDK reference ([Python](/sdks/python/reference#docs) or [JavaScript](/sdks/nodejs/reference#docs)) for more details on different operations you can perform on docs.
#### Embedding (vector) Search
Our embedding (vector) search functionality leverages machine learning to convert search queries and documents into numerical vectors, enabling semantic matching based on vector similarity. It utilizes an embedding space where similar vectors indicate related content and employs algorithms like k-nearest neighbors (KNN) to retrieve the most relevant documents. Key features include context awareness, flexibility for natural language queries, multi-modal search across various content types, and effective handling of synonyms.
**Parameters:**
| Parameter | Type | Description | Default |
| ----------------- | ---------- | ------------------------------------------------------------------------------------- | ------------ |
| `vector` | `number[]` | The embedding vector representing the semantic meaning of the query. | **Required** |
| `limit` | `integer` | The number of top results to return (must be between 1 and 50). | `10` |
| `lang` | `string` | The language for the search query. | `en-US` |
| `metadata_filter` | `object` | Filters to apply based on document metadata. | None |
| `mmr_strength` | `number` | The strength of Maximum Marginal Relevance diversification (must be between 0 and 1). | `0.5` |
| `confidence` | `number` | The confidence threshold for embedding similarity (must be between -1 and 1). | `0.5` |
The default parameters for embedding search are based on our internal benchmarking. These values provide a good starting point, but you may need to adjust them depending on your specific use case to achieve optimal results.
```python Python theme={"dark"}
# Define the vector query
vector_query = client.docs.embed(text="Medieval times in Europe").vectors[0]
# Search for docs
matched_docs = client.agents.docs.search(
agent_id="agent_id",
vector=vector_query,
limit=10, # the maximum number of docs to return
confidence=-0.3, # confidence range is -1 to 1
)
print(matched_docs.model_dump())
```
```javascript Node.js theme={"dark"}
// Define the vector query
const embedding_result = client.docs.embed({ text: "Medieval times in Europe" });
const vector_query = embedding_result.vectors[0];
// Search for docs
const matched_docs = await client.agents.docs.search({
agent_id: "agent_id",
vector: vector_query,
limit: 10, // the maximum number of docs to return
confidence: -0.3, // confidence range is -1 to 1
});
console.log(matched_docs);
```
```
{'docs': [{'id': '06791e86-6dcf-7816-8000-e4c3b781d05b',
'owner': {'id': '06791e82-46b7-739c-8000-3428f9d4e40f', 'role': 'agent'},
'snippet': {'content': 'The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa. In Europe, the era was marked by the rise of feudalism, the power of the Catholic Church, and the cultural blossoming of the Renaissance towards its end. Asia witnessed the flourishing of the Silk Road, facilitating trade and cultural exchange, and the rise of powerful empires such as the Mongol Empire, which at its height, stretched from Europe to Asia. Meanwhile, Africa saw the growth of influential kingdoms and empires like Mali, known for its wealth and the legendary pilgrimage of Mansa Musa, and the spread of Islam across the continent, which played a crucial role in shaping its cultural and social landscapes.',
'index': 0,
'embedding': [
-0.036940154,
0.021077264,
-0.03274468,
0.016222501,
-0.05038565,
0.012776218,
-0.034123193,
-0.005039564,
...
...]},
'distance': 0.9322964182738759,
'metadata': {"source": "https://en.wikipedia.org/wiki/Medieval_period"},
'title': 'Old World'}],
'time': 0.04166412353515625}
```
Check out the [API reference](/api-reference/search-agent-docs) or SDK reference ([Python](/sdks/python/reference#docs) or [JavaScript](/sdks/nodejs/reference#docs)) for more details on different operations you can perform on docs.
#### Hybrid Search
Our hybrid search functionality combines multiple search techniques to deliver highly relevant and accurate search results. Julep's hybrid search uses a three-pronged approach that leverages:
1. **Full-text search** - Traditional keyword-based search using PostgreSQL's tsquery/tsrank
2. **Vector search** - Semantic search using embeddings for contextual understanding
3. **Trigram search** - Fuzzy text matching using character n-grams for typo tolerance
By combining these approaches, hybrid search ensures that queries are matched not only on exact terms but also understood in context and tolerant of variations, providing more nuanced and precise results. This comprehensive approach enhances search performance, improves result relevance, and accommodates a wider range of search queries.
##### Trigram Search Features
Julep's trigram search capabilities include:
* **Fuzzy Matching** - Handles typos, spelling variations, and morphological differences
* **Similarity Scoring** - Combines trigram similarity with Levenshtein distance for accurate matching
* **Word-Level Analysis** - Matches individual meaningful words against target content
* **Adaptive Weighting** - Adjusts fuzzy matching strength based on query length
* **Performance Optimization** - Uses PostgreSQL's GIN indexes and materialized CTEs for efficient processing
##### How It Works
1. When a search query is submitted, Julep runs both full-text search and trigram-based fuzzy search in parallel.
2. Traditional full-text search results are prioritized (returned first).
3. Trigram search then finds documents that full-text search might miss due to minor variations or typos.
4. The system integrates these results with vector-based search results using Distribution-Based Score Fusion (DBSF).
5. Results are ranked and returned based on a combination of all three search approaches.
**Parameters:**
| **Parameter** | **Type** | **Description** | **Default** |
| ------------------------------ | ------------- | --------------------------------------------------------------------------------------------------- | ------------------ |
| `text` | `str` | The textual query to search within documents. | **Required** |
| `vector` | `List[float]` | The embedding vector representing the semantic meaning of the query. | **Required** |
| `alpha` | `float` | The weight assigned to embedding-based results versus text-based results (must be between 0 and 1). | `0.5` |
| `confidence` | `float` | The confidence threshold for embedding similarity (must be between -1 and 1). | `0.5` |
| `metadata_filter` | `object` | Filters to apply based on document metadata. | `None` |
| `limit` | `int` | The number of top results to return. | `3` |
| `lang` | `str` | The language to use for full-text search processing. | `english_unaccent` |
| `mmr_strength` | `float` | The strength of Maximum Marginal Relevance diversification (must be between 0 and 1). | `0.5` |
| `trigram_similarity_threshold` | `float` | The threshold for trigram similarity matching (must be between 0 and 1). | `0.6` |
| `k_multiplier` | `int` | Controls how many intermediate results to fetch before final scoring. | `7` |
The default parameters for hybrid search are based on our internal benchmarking. These values provide a good starting point, but you may need to adjust them depending on your specific use case to achieve optimal results.
**Example:**
```python Python theme={"dark"}
# Define the query
text_query = "Medieval times in Europe"
# Embed the query using the `docs.embed` method
embedded_query = client.docs.embed(text=text_query).vectors[0]
# Search for docs
matched_docs = client.agents.docs.search(
agent_id="agent_id",
text=text_query,
alpha=0.5, # the weight of the embedding query
vector=embedded_query,
confidence=-0.3, # confidence range is -1 to 1
)
print(matched_docs.model_dump())
```
```javascript Node.js theme={"dark"}
// Define the query
const text_query = "Medieval times in Europe";
// Embed the query using the `docs.embed` method
const embedding_result = await client.docs.embed({ text: text_query });
const embedded_query = embedding_result.vectors[0];
// Search for docs
const matched_docs = await client.agents.docs.search({
agent_id: "agent_id",
text: text_query,
vector: embedded_query,
confidence: -0.3, // confidence range is -1 to 1
alpha: 0.5, // the weight of the embedding query
});
console.log(matched_docs);
```
```
{'docs': [{'id': '06791e86-6dcf-7816-8000-e4c3b781d05b',
'owner': {'id': '06791e82-46b7-739c-8000-3428f9d4e40f', 'role': 'agent'},
'snippet': {'content': 'The medieval period, spanning roughly from the 5th to the late 15th century, was a time of significant transformation across Europe, Asia, and Africa. In Europe, the era was marked by the rise of feudalism, the power of the Catholic Church, and the cultural blossoming of the Renaissance towards its end. Asia witnessed the flourishing of the Silk Road, facilitating trade and cultural exchange, and the rise of powerful empires such as the Mongol Empire, which at its height, stretched from Europe to Asia. Meanwhile, Africa saw the growth of influential kingdoms and empires like Mali, known for its wealth and the legendary pilgrimage of Mansa Musa, and the spread of Islam across the continent, which played a crucial role in shaping its cultural and social landscapes.',
'index': 0,
'embedding': [
-0.036940154,
0.021077264,
-0.03274468,
0.016222501,
-0.05038565,
0.012776218,
-0.034123193,
-0.005039564,
...
...]},
'distance': 0.13411101466087272,
'metadata': {"source": "https://en.wikipedia.org/wiki/Medieval_period"},
'title': 'Old World'}],
'time': 0.0514223575592041}
```
Check out the [API reference](/api-reference/search-agent-docs) or SDK reference ([Python](/sdks/python/reference#docs) or [JavaScript](/sdks/nodejs/reference#docs)) for more details on different operations you can perform on docs.
## Filtering
While search is carried on based on the textual and/or semantic content of the documents, you can also filter the documents based on their metadata.
**Example:**
```python Python theme={"dark"}
# Filter docs based on metadata
dev_client.agents.docs.list(
agent_id="agent_id",
metadata_filter={"source": "wikipedia"},
)
print(docs.items)
```
```javascript Node.js theme={"dark"}
// Filter docs based on metadata
const docs = await client.agents.docs.list(
agentId,
{
metadata_filter: {"source": "wikipedia"},
}
);
console.log(docs.items);
```
Check out the [API reference](/api-reference/list-agent-docs) or SDK reference ([Python](/sdks/python/reference#docs) or [JavaScript](/sdks/nodejs/reference#docs)) for more details on different operations you can perform on docs.
## Relationship to Other Concepts
### Sessions
Sessions have access to search, retrieve and reference agents and users documents inside chat conversations. Read more about it [here](/concepts/sessions#documents).
### Tasks
By leveraging [System Tools](/concepts/tools#system-tools), Julep [Tasks](/concepts/tasks) have the ability to create, search, filter and read documents.
**Example:**
```yaml [expandable] theme={"dark"}
input_schema:
type: object
properties:
user_id:
type: string
description: The id of the user to list documents for
tools:
- name: "list_user_docs"
description: "List all documents for the current user"
type: "system"
system:
resource: user
subresource: doc
operation: list
main:
# Step that lists all documents for the current user
- tool: "list_user_docs"
arguments:
user_id: $ _.user_id
# Step that evaluates the textual contents of all documents for the current user
- evaluate:
all_user_docs_contents: $ [doc.content for doc in _.items]
```
Checkout [this cookbook](https://colab.research.google.com/github/julep-ai/julep/blob/dev/cookbooks/11-generate-user-personas.ipynb) that leverages Julep's docs, system tools and tasks to create content-rich user personas.
## Best Practices
- **1. Metadata**: Use consistent and descriptive metadata to enhance document retrieval and filtering.
- **1. Version Control**: Maintain version control for documents to track changes and updates over time.
- **1. Access Control**: Ensure sensitive information is protected and access to documents is properly managed.
- **1. Chunking Strategies**: Implement efficient chunking strategies to optimize document processing and retrieval.
- **1. Update**: Regularly update document content and metadata to keep information current and relevant.
## Advanced Search Features
### Trigram-Enhanced Fuzzy Matching
Julep's advanced fuzzy matching capability is built on PostgreSQL's `pg_trgm` extension and enhanced with additional similarity techniques. This allows for resilient document retrieval that can handle variations in text, including:
* Typos and spelling errors
* Morphological variations
* Term order differences
* Incomplete terms
#### Similarity Mechanisms
Julep uses a multi-layered approach to determine text similarity:
1. **Basic Trigram Similarity** - Uses PostgreSQL's built-in trigram functions to match documents based on character-level n-grams.
2. **Enhanced Similarity** - Combines trigram matching with Levenshtein distance calculations to provide better accuracy, especially for shorter text segments:
```sql theme={"dark"}
-- 70% trigram, 30% Levenshtein for shorter strings
RETURN 0.7 * trgm_sim + 0.3 * norm_lev;
```
3. **Word-Level Similarity** - Breaks text into individual words and finds the best match for each meaningful word:
```sql theme={"dark"}
-- Only process meaningful words (longer than 2 chars)
IF length(words1[i]) > 2 THEN
-- Find best match in target content
best_match := GREATEST(best_match, similarity(words1[i], words2[j]));
```
4. **Comprehensive Similarity** - Adaptively weights different similarity metrics based on query characteristics:
```sql theme={"dark"}
-- Weight factor based on query length - shorter queries need more help
word_weight float := CASE
WHEN length(query) < 10 THEN 0.4
WHEN length(query) < 20 THEN 0.3
ELSE 0.2
END;
```
#### Tuning Search Behavior
You can customize Julep's fuzzy search behavior through several parameters:
* **Similarity Threshold** (`trigram_similarity_threshold`) - Controls the minimum similarity score required for a document to match:
* Higher values (e.g., 0.8) require closer matches, reducing false positives but may miss relevant documents with variations
* Lower values (e.g., 0.3) are more lenient, catching more variations but potentially including less relevant results
* Default: 0.6 for hybrid search, 0.3 for text-only search
* **Alpha Weight** (`alpha`) - Balances the importance of vector-based semantic search vs. text-based search:
* Higher values prioritize semantic/embedding matches
* Lower values prioritize text and trigram matches
* Default: 0.7 (70% weight to embeddings)
* **Search Language** (`lang`) - Affects tokenization, stemming, and other text processing operations:
* Default: 'english\_unaccent' which handles accent/diacritic-insensitive matching
#### Implementation Details
Julep's trigram search is implemented using:
1. **Database Indexes** - GIN indexes on document title and content for efficient trigram operations:
```sql theme={"dark"}
CREATE INDEX IF NOT EXISTS idx_docs_title_trgm ON docs USING GIN (title gin_trgm_ops);
CREATE INDEX IF NOT EXISTS idx_docs_content_trgm ON docs USING GIN (content gin_trgm_ops);
```
2. **Materialized CTEs** - Improves performance for complex query operations:
```sql theme={"dark"}
WITH tsv_results AS MATERIALIZED (...)
```
3. **Runtime Optimizations** - Selective application of more expensive calculations:
```sql theme={"dark"}
-- Only compute Levenshtein for reasonable length strings (performance)
IF length(text1) <= 50 AND length(text2) <= 50 THEN
```
4. **Distribution-Based Score Fusion** - Combines results from different search methods:
```sql theme={"dark"}
-- Aggregate all text/embedding scores into arrays
aggregated AS (
SELECT
array_agg(text_score ORDER BY rn) AS text_scores,
array_agg(embedding_score ORDER BY rn) AS embedding_scores
```
These technologies combine to provide a sophisticated fuzzy search capability that significantly improves document retrieval compared to traditional search methods.
## Next Steps
* [Sessions](/concepts/sessions) - Learn about sessions and how documents are used in chat conversations.
* [Tools](/concepts/tools) - Learn about tools and how they can be used to fill documents with content.
* [Tasks](/concepts/tasks) - Learn about tasks and how to use documents inside tasks.
* [Cookbooks](https://github.com/julep-ai/julep/tree/main/cookbooks) - Check out cookbooks to see how Julep can be used in real-world scenarios.
# Executions
Source: https://docs.julep.ai/concepts/execution
Understanding Task Executions and Their Lifecycle
## Overview
Executions in Julep represent instances of tasks that have been initiated with specific inputs. They embody the lifecycle of a task, managing its progression through various states from initiation to completion. Understanding executions is crucial for effectively managing and monitoring the behavior of your AI agents and their workflows.
## Components
Executions are comprised of several key components that work together to manage and monitor the state of a task:
* **Execution ID**: A unique identifier for each execution instance.
* **Task ID**: The identifier of the task being executed.
* **Input**: The inputs provided to the task at the time of execution.
* **Status**: The current state of the execution (e.g., queued, running, succeeded).
* **Output**: The result produced by the execution upon completion.
* **Transitions**: The sequence of state changes that the execution undergoes.
* **Transition Count**: The number of transitions that have occurred in this execution.
### Execution Configuration options
| Option | Type | Description | Default |
| ------------------ | -------- | ---------------------------------------------- | ------------ |
| `task_id` | `string` | The ID of the task to execute | **Required** |
| `input` | `object` | The input to the task | **Required** |
| `metadata` | `object` | Additional metadata for the execution instance | `null` |
| `transition_count` | `number` | The number of transitions in this execution | `null` |
## Lifecycle of an Execution
An execution follows a well-defined lifecycle, transitioning through various states from start to finish. Understanding these states helps in monitoring and managing task executions effectively.
### Execution Statuses
Executions can exist in one of the following statuses:
| **Status** | **Description** |
| ---------------- | ------------------------------------------------------------- |
| `queued` | The execution is queued and waiting to start. |
| `starting` | The execution is starting. |
| `running` | The execution is currently running. |
| `awaiting_input` | The execution is suspended and awaiting user input to resume. |
| `succeeded` | The execution has completed successfully. |
| `failed` | The execution has failed due to an error. |
| `cancelled` | The execution has been cancelled by the user or system. |
### Execution State Machine
The state transitions of an execution are governed by a state machine that ensures proper progression and handling of different scenarios.
```mermaid theme={"dark"}
stateDiagram-v2
[*] --> queued
queued --> starting
queued --> cancelled
starting --> cancelled
starting --> failed
starting --> running
running --> running
running --> awaiting_input
running --> cancelled
awaiting_input --> running
awaiting_input --> cancelled
running --> succeeded
failed --> [*]
succeeded --> [*]
cancelled --> [*]
```
## Execution State Transitions
Executions in Julep follow a specific state transition model. The transitions are governed by both the execution status and the transition type:
* **Init**: The execution is initialized.
* **Start**: The execution begins.
* **Step**: A step within the execution is executed.
* **Wait**: The execution is waiting for an external input.
* **Resume**: The execution resumes after waiting.
* **Finish**: The execution completes successfully.
* **Error**: The execution encounters an error.
* **Cancel**: The execution is cancelled.
### Transition Types
| **Transition Type** | **Description** |
| ------------------- | ---------------------------------------------- |
| `init` | Initializes the execution. |
| `start` | Starts the execution process. |
| `step` | Executes a step within the task. |
| `wait` | Pauses execution waiting for external input. |
| `resume` | Resumes execution after a wait. |
| `finish` | Marks the execution as successfully completed. |
| `error` | Marks the execution as failed due to an error. |
| `cancel` | Cancels the execution. |
## Creating an Execution
To create an execution for a specific task, use the following method in the SDKs.
```python Python theme={"dark"}
from julep import Julep
import yaml
client = Julep(api_key="YOUR_API_KEY")
# Execute the task (assuming the task is already created)
execution = client.executions.create(
task_id="task_id",
input={
"parameter1": "value1",
"parameter2": "value2"
}
)
print(f"Execution ID: {execution.id}")
```
```javascript Node.js theme={"dark"}
const { Julep } = require('@julep/sdk');
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
// Execute the task (assuming the task is already created)
const execution = await client.executions.create({
task_id: "task_id",
input: {
parameter1: 'value1',
parameter2: 'value2'
}
});
console.log("Execution ID:", execution.id);
```
Check out the API reference [here](/api-reference/executions) or SDK reference (Python [here](/sdks/python/reference#executions) or JavaScript [here](/sdks/nodejs/reference#executions) for more details on different operations you can perform on executions.
## Monitoring an Execution
After initiating an execution, it's essential to monitor its progress and handle its completion or failure appropriately.
```python Python theme={"dark"}
import time
from julep import Julep
client = Julep(api_key="YOUR_API_KEY")
execution_id = "YOUR_EXECUTION_ID"
while True:
result = client.executions.get(execution_id)
print(f"Status: {result.status}")
print(f"Current output: {result.output}")
if result.status in ["succeeded", "failed", "cancelled"]:
if result.status == "succeeded":
print("Execution succeeded with output:", result.output)
else:
print("Execution ended with status:", result.status)
break
time.sleep(5) # Wait for 5 seconds before polling again
```
```javascript Node.js theme={"dark"}
const { Julep } = require('@julep/sdk');
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const executionId = 'YOUR_EXECUTION_ID';
while (true) {
const result = await client.executions.get(executionId);
console.log(`Status: ${result.status}`);
console.log(`Current output: ${result.output}`);
if (["succeeded", "failed", "cancelled"].includes(result.status)) {
if (result.status === "succeeded") {
console.log("Execution succeeded with output:", result.output);
} else {
console.log("Execution ended with status:", result.status);
}
break;
}
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds before polling again
}
```
To view more details about the status of the execution and how it is transitioning between states, you can use list the transitions of an execution.
Example:
```python Python theme={"dark"}
from julep import Julep
client = Julep(api_key="YOUR_API_KEY")
execution_id = "YOUR_EXECUTION_ID"
transitions = client.executions.transitions.list(execution_id)
print(transitions.items)
```
```javascript Node.js theme={"dark"}
const { Julep } = require('@julep/sdk');
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const executionId = 'YOUR_EXECUTION_ID';
const transitions = await client.executions.transitions.list(executionId);
console.log(transitions.items);
```
Check out the API reference [here](/api-reference/executions) or SDK reference (Python [here](/sdks/python/reference#executions) or JavaScript [here](/sdks/nodejs/reference#executions) for more details on different operations you can perform on executions.
## Streaming Execution Status Updates
### Using the raw SSE endpoint
You can subscribe to real-time status updates using the Server-Sent Events (SSE) endpoint. Each event conforms to the `ExecutionStatusEvent` schema and includes the following fields:
* **execution\_id**: The UUID of the execution.
* **status**: The current execution status.
* **updated\_at**: ISO 8601 timestamp of the update.
* **error**: Error message if the execution failed.
* **transition\_count**: Number of transitions that have occurred.
* **metadata**: Arbitrary metadata for the event.
```bash theme={"dark"}
curl -X GET 'https://api.julep.ai/api/executions/{execution_id}/status.stream' \
-H 'Authorization: Bearer $JULEP_API_KEY'
```
You'll be getting events that look like this:
```bash theme={"dark"}
data: {"execution_id":"068306ff-e0f3-7fe9-8000-0013626a759a","status":"starting","updated_at":"2025-05-23T12:54:24.565424Z","error":null,"transition_count":1,"metadata":{}}
data: {"execution_id":"068306ff-e0f3-7fe9-8000-0013626a759a","status":"running","updated_at":"2025-05-23T12:54:30.903484Z","error":null,"transition_count":2,"metadata":{}}
data: {"execution_id":"068306ff-e0f3-7fe9-8000-0013626a759a","status":"succeeded","updated_at":"2025-05-23T12:56:12.054067Z","error":null,"transition_count":3,"metadata":{}}
```
### Using the Python SDK `AsyncClient`
```python Python theme={"dark"}
from julep import AsyncClient
client = AsyncClient(api_key="YOUR_API_KEY")
execution_id = "YOUR_EXECUTION_ID"
# Subscribe to the live status stream (async generator)
status_stream = await client.executions.status.stream(execution_id=execution_id)
# Consume events in real-time using async for
async for event in status_stream:
print("Execution status:", event.status, "updated at", event.updated_at)
```
This approach relies on Python's async / await syntax. Make sure to:
1. Use `AsyncClient` not `Client`.
2. `await client.executions.status.stream(...)` to obtain the async generator.
3. Iterate with `async for` to consume events.
## Updating/Cancelling an Execution
To update or cancel an execution, you can use the `change_status` method in the SDKs.
Example:
```python Python theme={"dark"}
from julep import Julep
client = Julep(api_key="YOUR_API_KEY")
execution_id = "YOUR_EXECUTION_ID"
# To cancel an execution
client.executions.change_status(execution_id=execution_id, status="cancelled")
# To resume an execution with specific input
client.executions.change_status(
execution_id=execution_id,
status="running",
input={
"parameter1": "value1",
"parameter2": "value2"
}
)
```
```javascript Node.js theme={"dark"}
const { Julep } = require('@julep/sdk');
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const executionId = 'YOUR_EXECUTION_ID';
// To cancel an execution
await client.executions.changeStatus(executionId, 'cancelled');
// To resume an execution with specific input
await client.executions.changeStatus(
executionId,
'running',
{
input: {
"parameter1": "value1",
"parameter2": "value2"
}
}
);
```
Check out the API reference [here](/api-reference/executions) or SDK reference (Python [here](/sdks/python/reference#executions) or JavaScript [here](/sdks/nodejs/reference#executions) for more details on different operations you can perform on executions.
## Best Practices
- **1. Execution Statuses**: Ensure your application gracefully handles all possible execution statuses, including `failed` and `cancelled`.
- **1. Polling Interval**: Choose an appropriate polling interval to balance responsiveness and API usage.
- **1. Logging**: Maintain detailed logs of execution statuses and outputs for auditing and debugging purposes.
## Next Steps
* [Checkout the Tutorial](/tutorials) - Learn how to use executions in a tutorial
* [Checkout the Execution Lifecycle](/advanced/lifecycle) - Learn more about the execution lifecycle
# Files
Source: https://docs.julep.ai/concepts/files
Managing files and attachments in Julep
# Files
Files in Julep allow agents to work with various types of data including documents, images, audio, and other media. Files are stored securely and can be accessed by agents and tasks as needed.
## Overview
Julep's file system provides:
* Secure storage for various file types
* Unique identifiers for consistent access
* Metadata for organization and discovery
* Content hashing for integrity verification
* Project association for logical grouping
## File Properties
Each file in Julep has the following properties:
| **Field** | **Type** | **Description** | **Default** |
| ------------- | -------- | ------------------------------------------------------ | --------------- |
| `name` | `string` | Name of the file | Required |
| `project` | `string` | The canonical name of the project this file belongs to | `"default"` |
| `content` | `string` | Base64-encoded file content | Required |
| `description` | `string` | Description of the file | `""` |
| `mime_type` | `string` | MIME type of the file | `null` |
| `size` | `number` | Size of the file in bytes (read-only) | Auto-calculated |
| `hash` | `string` | Hash of the file content (read-only) | Auto-calculated |
| `created_at` | `string` | Creation timestamp (read-only) | Auto-generated |
## Creating Files
You can create files using the Julep SDK in Python or JavaScript:
```python Python theme={"dark"}
from julep import Julep
import base64
client = Julep(api_key="your_api_key")
# Read a local file and encode it as base64
with open("path/to/document.pdf", "rb") as file:
content = base64.b64encode(file.read()).decode("utf-8")
# Create the file in Julep
file = client.files.create(
name="document.pdf",
project="knowledge-base",
content=content,
description="Important document for reference",
mime_type="application/pdf"
)
print(f"Created file: {file.id}")
```
```javascript Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
import fs from 'fs';
const client = new Julep({ apiKey: 'your_api_key' });
// Read a local file and encode it as base64
const content = fs.readFileSync('path/to/document.pdf').toString('base64');
// Create the file in Julep
const file = await client.files.create({
name: "document.pdf",
project: "knowledge-base",
content: content,
description: "Important document for reference",
mime_type: "application/pdf"
});
console.log(`Created file: ${file.id}`);
```
## Managing Files
### Retrieving Files
```python Python theme={"dark"}
# Get a specific file by ID
file = client.files.get("file_id_here")
print(file)
# List all files
files = client.files.list()
for file in files:
print(f"{file.name}: {file.description}")
# List files with filtering
files = client.files.list(metadata_filter={"category": "report"})
```
```javascript Node.js theme={"dark"}
// Get a specific file by ID
const file = await client.files.get("file_id_here");
console.log(file);
// List all files
const files = await client.files.list();
files.forEach(file => console.log(`${file.name}: ${file.description}`));
// List files with filtering
const files = await client.files.list({ metadata_filter: { category: "report" } });
```
### Deleting Files
```python Python theme={"dark"}
# Delete a file
client.files.delete("file_id_here")
```
```javascript Node.js theme={"dark"}
// Delete a file
await client.files.delete("file_id_here");
```
## Relationship to Other Concepts
### Projects
Files belong to exactly one project, which helps organize related resources together. When creating a file, you can specify which project it belongs to using the `project` parameter. If not specified, the file will be assigned to the "default" project.
**Example:**
```python Python theme={"dark"}
# Create a file in a specific project
file = client.files.create(
name="marketing-image.jpg",
project="product-launch",
content=base64_content,
mime_type="image/jpeg"
)
```
```javascript Node.js theme={"dark"}
// Create a file in a specific project
const file = await client.files.create({
name: "marketing-image.jpg",
project: "product-launch",
content: base64Content,
mime_type: "image/jpeg"
});
```
For more information about projects, see [Projects](/concepts/projects).
### Agents and Tasks
Files can be used by both agents and tasks to access and process information. For example, an agent might analyze an image file, or a task might process a document file.
```python Python theme={"dark"}
# Create a file
file = client.files.create(name="data.csv", content=base64_content)
# Reference the file in a task execution
execution = client.tasks.executions.create(
task_id="task_id_here",
input={"file_id": file.id}
)
```
```javascript Node.js theme={"dark"}
// Create a file
const file = await client.files.create({
name: "data.csv",
content: base64Content
});
// Reference the file in a task execution
const execution = await client.tasks.executions.create({
task_id: "task_id_here",
input: { file_id: file.id }
});
```
## Best Practices
- Group related files in the same project
- Use consistent naming conventions
- Include descriptive metadata for better discovery
- Keep file sizes reasonable for faster processing
- Consider chunking large files into smaller ones
- Be mindful of the content limits when Base64 encoding
- Avoid storing sensitive information in files
- Regularly audit and clean up unused files
- Validate file types before uploading
## Next Steps
* [Projects](/concepts/projects) - Learn about organizing resources with projects
* [Agents](/concepts/agents) - Learn how agents can work with files
* [Tasks](/concepts/tasks) - Learn how to use files in task workflows
# Projects
Source: https://docs.julep.ai/concepts/projects
Organizational units for grouping related resources
## Overview
Projects are organizational units that allow you to group related agents, users, files, and other resources together. They provide a way to manage resources at a higher level and maintain clean separation between different use cases or applications.
Projects provide several benefits:
* **Organization**: Group related resources together logically
* **Management**: Administer resources at the project level
* **Isolation**: Keep resources separate between different applications
* **Deployment**: Deploy multiple templates or configurations without conflicts
Each developer automatically gets a "default" project that contains all existing resources. This ensures backward compatibility with existing applications while providing the benefits of project organization.
## Project Properties
A project includes:
* `id`: Unique identifier
* `canonical_name`: Machine-readable name (unique per developer)
* `name`: Human-readable display name
* `metadata`: Custom attributes for the project
## Project Relationships
Projects have a one-to-many relationship with resources:
* Each agent belongs to exactly one project
* Each user belongs to exactly one project
* Each file belongs to exactly one project
When creating resources, you can specify which project they belong to using the `project` field. If not specified, resources are automatically assigned to the "default" project.
## Default Project
Every developer automatically receives a "default" project when they first interact with the API. This project:
* Cannot be deleted
* Has a canonical name of "default"
* Contains all previously created resources (pre-dating the projects feature)
* Serves as a fallback for resources created without a project specification
## API Operations
Projects support standard CRUD operations:
* `GET /projects`: List all projects for a developer
* `POST /projects`: Create a new project
* `GET /projects/{id}`: Retrieve a specific project
* `PUT /projects/{id}`: Update a project
* `PATCH /projects/{id}`: Partially update a project
* `DELETE /projects/{id}`: Delete a project (except the default project)
## Usage Examples
### Creating a Project
```json theme={"dark"}
POST /projects
{
"name": "Customer Support Bot",
"canonical_name": "support-bot",
"metadata": {
"description": "Resources for our customer support chatbot",
"team": "customer-success"
}
}
```
### Creating a Resource in a Project
When creating a resource such as an agent, specify the project canonical name:
```json theme={"dark"}
POST /agents
{
"name": "Support Assistant",
"project": "support-bot",
"instructions": [
"You are a helpful customer support assistant."
],
"model": "gpt-4-turbo"
}
```
## Future Capabilities
Projects are designed with future extensibility in mind. Planned enhancements include:
* Project-specific API keys for more granular access control
* Usage tracking and billing at the project level
* Enhanced permissions and role-based access control
* Cross-project resource sharing capabilities
## Best Practices
* Use meaningful canonical names for projects that reflect their purpose
* Group resources by logical application or team
* Use metadata to add custom attributes for filtering and organization
* Create separate projects for development, staging, and production environments
# Secrets
Source: https://docs.julep.ai/concepts/secrets
Securely store and manage sensitive information for your LLM applications
# Secrets
Secrets allow you to securely store and manage sensitive information like API keys, credentials, and other confidential data that your agents and tasks need to access external services.
## What are Secrets?
Secrets are encrypted key-value pairs that can be referenced in your tasks and used by your agents without exposing the actual values in your code or configuration files. This helps maintain security and separates sensitive data from your application logic.
## Key Features
* **Encrypted Storage**: All secrets are encrypted at rest using industry-standard AES-256 encryption
* **Access Control**: Secrets are scoped to developers and cannot be accessed across developer accounts
* **Named References**: Reference secrets by name in tasks and tools instead of hardcoding values
* **Versioning**: Track when secrets were created and updated
* **Metadata Support**: Add custom metadata to organize and categorize your secrets
## Common Use Cases
* Storing API keys for external services (OpenAI, Google, AWS, etc.)
* Managing database credentials
* Securing authentication tokens
* Storing sensitive configuration values
* Managing encrypted communication channels
## Secrets vs. Environment Variables
While environment variables are commonly used for configuration, secrets provide several advantages:
* **Encrypted Storage**: Environment variables are stored in plain text, while secrets are encrypted
* **Access Management**: Environment variables are global, while secrets have access controls
* **Audit Trail**: Secrets maintain creation and update timestamps
* **Organized Management**: The secrets API provides a structured way to manage sensitive data
## Working with Secrets
### Creating Secrets
Secrets can be created through the API, SDK, or CLI:
```python Python theme={"dark"}
secret = client.secrets.create(
name="stripe_api_key",
value="sk_test_...",
description="Stripe API key for payment processing",
metadata={"environment": "production", "owner": "payments-team"}
)
print(f"Created secret: {secret.name}")
```
```bash CLI theme={"dark"}
julep secrets create --name "openai_api_key" --value "sk-..." --description "OpenAI API key"
```
```bash API theme={"dark"}
curl https://dev.julep.ai:443/api/secrets \
--request POST \
--header 'Content-Type: application/json' \
--data '{
"metadata": {},
"name": "openai_api_key",
"description": "",
"value": "sk-..."
}'
```
#### Required Parameters
* `name`: The name of the secret (must be a valid identifier)
* `value`: The secret value to encrypt and store
#### Optional Parameters
* `description`: A description of what the secret is used for
* `metadata`: A dictionary of metadata to associate with the secret
### Using Secrets in Tasks
Once created, secrets can be referenced in your tasks:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: openai
operation: completion
arguments:
prompt: "Summarize this text"
secret_name: openai_api_key
```
### Using Secrets in Expressions
You can also reference secrets in expressions:
```yaml theme={"dark"}
steps:
- kind: prompt
model: gpt-4
prompt: "Generate a summary"
template_variables:
api_url: "$ f'https://api.example.com/v1?key={secrets.api_key}'"
```
## Secret Names and Conventions
Secret names must:
* Begin with a letter
* Contain only alphanumeric characters and underscores
* Be unique within a developer account
Good naming conventions:
* Use descriptive names like `stripe_api_key` instead of just `api_key`
* Include service names in the key name
* Use consistent formatting (snake\_case recommended)
## Next Steps
* [Using Secrets in Julep](/guides/using-secrets) - Step-by-step guide for using secrets
* [Secrets Management](/advanced/secrets-management) - Advanced guide for managing secrets
* [API Reference](/api-reference#tag/secrets) - Complete API reference for secrets
# Sessions
Source: https://docs.julep.ai/concepts/sessions
Understanding Julep Sessions and state management
## Overview
Sessions in Julep are the backbone of stateful interactions between users and agents. They maintain the context and history of conversations, enabling personalized and coherent interactions over extended periods. Whether it's handling ongoing customer support inquiries or having a conversation with a user, sessions ensure that the agent retains necessary information to provide meaningful responses.
## Components
Sessions are comprised of several key components that work together to manage state and context:
* **Session ID**: A unique identifier (`uuid7`) for each session.
* **User**: The individual or entity interacting with the agent, represented by its id.
* **Agent**: The AI entity interacting with the user within the session, represented by its id.
* **History**: The history of the conversation, which the agent uses to generate relevant responses.
* **System Template**: A specific system prompt template that sets the background for this session.
* **Situation**: A description of the current situation for the session.
* **Metadata**: Additional data associated with the session, such as user preferences, session preferences, and other relevant information.
### Session Configuration Options
When creating a session, you can leverage the following configuration options to tailor the experience:
| Option | Type | Description | Default |
| -------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------- | ----------------- |
| `agent` | `UUID` \| `None` | The ID of the agent to associate with the session | `None` |
| `user` | `UUID` \| `None` | The ID of the user interacting with the agent | `None` |
| `context_overflow` | `truncate` \| `adaptive` \| `None` | Strategy for handling context overflow: `truncate` cuts off the oldest context; `adaptive` adjusts dynamically | `None` |
| `metadata` | `object` \| `None` | Additional metadata for the session (e.g., user preferences) | `None` |
| `system_template` | `str` \| `None` | A specific system prompt template that sets the background for this session | `None` |
| `render_templates` | `StrictBool` | Whether to render system and assistant messages as Jinja templates | `True` |
| `token_budget` | `int` \| `None` | Threshold value for the adaptive context functionality | `None` |
| `auto_run_tools` | `StrictBool` | Whether to automatically execute tools and send the results back to the model when available | `False` |
| `forward_tool_calls` | `StrictBool` | Whether to forward tool calls directly to the model | `False` |
| `recall_options` | `object` \| `None` | Options for different RAG search modes (VectorDocSearch, TextOnlyDocSearch, HybridDocSearch) in the session | `VectorDocSearch` |
### Recall Options (RAG Search)
When configuring a session, you can specify recall options to control how context or certain data is recalled during the session. Below are the available options based on search mode:
| Parameter | Type | Description | Default |
| --------------------- | ------------------- | ---------------------------------------------------------- | ---------- |
| `mode` | `Literal["vector"]` | The mode to use for the search (must be "vector") | `"vector"` |
| `lang` | `str` | The language for text search (other languages coming soon) | `en-US` |
| `limit` | `int` | The limit of documents to return (1-50) | `10` |
| `max_query_length` | `int` | The maximum query length (100-10000 characters) | `1000` |
| `metadata_filter` | `object` | Metadata filter to apply to the search | `{}` |
| `num_search_messages` | `int` | The number of search messages to use for the search (1-50) | `4` |
| `confidence` | `float` | The confidence cutoff level (-1 to 1) | `0.5` |
| `mmr_strength` | `float` | MMR Strength (mmr\_strength = 1 - mmr\_lambda) (0 to 1) | `0.5` |
| Parameter | Type | Description | Default |
| ------------------------------ | ----------------- | ----------------------------------------------------------------------- | -------- |
| `mode` | `Literal["text"]` | The mode to use for the search (must be "text") | `"text"` |
| `lang` | `str` | The language for text search (other languages coming soon) | `en-US` |
| `limit` | `int` | The limit of documents to return (1-50) | `10` |
| `max_query_length` | `int` | The maximum query length (100-10000 characters) | `1000` |
| `metadata_filter` | `object` | Metadata filter to apply to the search | `{}` |
| `num_search_messages` | `int` | The number of search messages to use for the search (1-50) | `4` |
| `trigram_similarity_threshold` | `float` | The threshold for trigram similarity matching (must be between 0 and 1) | `0.6` |
| Parameter | Type | Description | Default |
| ------------------------------ | ------------------- | ------------------------------------------------------------------------------ | ------------------ |
| `mode` | `Literal["hybrid"]` | The mode to use for the search (must be "hybrid") | `"hybrid"` |
| `lang` | `str` | The language for text search (other languages coming soon) | `english_unaccent` |
| `limit` | `int` | The limit of documents to return (1-50) | `10` |
| `max_query_length` | `int` | The maximum query length (100-10000 characters) | `1000` |
| `metadata_filter` | `object` | Metadata filter to apply to the search | `{}` |
| `num_search_messages` | `int` | The number of search messages to use for the search (1-50) | `4` |
| `alpha` | `float` | Weight between text-based and vector-based search (0=pure text, 1=pure vector) | `0.5` |
| `confidence` | `float` | The confidence cutoff level (-1 to 1) | `0.5` |
| `mmr_strength` | `float` | MMR Strength (mmr\_strength = 1 - mmr\_lambda) (0 to 1) | `0.5` |
| `trigram_similarity_threshold` | `float` | The threshold for trigram similarity matching (must be between 0 and 1) | `0.6` |
| `k_multiplier` | `int` | Controls how many intermediate results to fetch before final scoring | `7` |
* When `recall_options` is not explicitly set (for instance, it is `None`), `vector` search mode is used with default parameters.
* The default parameters for each search mode are based on our internal benchmarking. These values provide a good starting point, but you may need to adjust them depending on your specific use case to achieve optimal results.
**Hybrid Search with Trigram Support**
Julep's hybrid search combines multiple search techniques:
1. **Traditional full-text search** using PostgreSQL's tsquery/tsrank for keyword matching
2. **Vector-based semantic search** using embeddings for contextual understanding
3. **Trigram fuzzy matching** for handling typos, spelling variations, and morphological differences
The trigram search capability uses PostgreSQL's pg\_trgm extension enhanced with Levenshtein distance calculations to provide resilient document retrieval even when search terms contain variations or errors. This is especially useful for natural language queries that may contain typos or alternative word forms.
You can control the fuzzy matching behavior using the `trigram_similarity_threshold` parameter - higher values (e.g., 0.8) require closer matches while lower values (e.g., 0.3) are more lenient. For more details on the advanced search capabilities, see the [Documents (RAG)](/concepts/docs#advanced-search-features) section.
### System Template
The **System Template** is a specific system prompt written as a Jinja template that sets the foundational context and instructions for the agent within a session. It defines the background, directives, and any relevant information that the agent should consider when interacting with the user.
For a comprehensive guide on system templates including available variables, customization options, and advanced usage patterns, see the [System Templates](/advanced/system-templates) documentation. For more details on Jinja templates, refer to the [Jinja documentation](https://jinja.palletsprojects.com/).
```python [expandable] theme={"dark"}
{%- if agent.name -%}
You are {{agent.name}}.{{" "}}
{%- endif -%}
{%- if agent.about -%}
About you: {{agent.about}}.{{" "}}
{%- 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 -%}
{{NEWLINE}}
{%- if session.situation -%}
Situation: {{session.situation}}
{%- endif -%}
{{NEWLINE+NEWLINE}}
{%- if agent.instructions -%}
Instructions:{{NEWLINE}}
{%- if agent.instructions is string -%}
{{agent.instructions}}{{NEWLINE}}
{%- else -%}
{%- for instruction in agent.instructions -%}
- {{instruction}}{{NEWLINE}}
{%- endfor -%}
{%- endif -%}
{{NEWLINE}}
{%- endif -%}
{%- if docs -%}
Relevant documents:{{NEWLINE}}
{%- for doc in docs -%}
{{doc.title}}{{NEWLINE}}
{%- if doc.content is string -%}
{{doc.content}}{{NEWLINE}}
{%- else -%}
{%- for snippet in doc.content -%}
{{snippet}}{{NEWLINE}}
{%- endfor -%}
{%- endif -%}
---
{%- endfor -%}
{%- endif -%}
```
## How to Use Sessions
Sessions are integral to maintaining a continuous and coherent interaction between users and agents. Here's how to create and manage sessions using Julep's SDKs.
### Creating a Session
Here are examples of how to create a session using the SDKs:
```python Python theme={"dark"}
client = Julep(api_key="YOUR_API_KEY")
session = client.sessions.create(
agent="agent_id",
user="user_id",
context_overflow="adaptive",
)
```
```javascript Node.js theme={"dark"}
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const session = await client.sessions.create({
agent_id: "agent_id",
user_id: "user_id",
context_overflow: "adaptive",
});
```
Check out the [API reference](/api-reference/sessions) or SDK reference ([Python](/sdks/python/reference#sessions) or [JavaScript](/sdks/nodejs/reference#sessions)) for more details on different operations you can perform on sessions.
### Chatting in a Session
Once a session is created, you can engage in a conversation by sending messages to the agent within that session.
```python Python theme={"dark"}
client = Julep(api_key="YOUR_API_KEY")
response = client.sessions.chat(
session_id="SESSION_ID",
messages=[
{
"role": "user", # or "system"
"content": "YOUR_MESSAGE"
}
]
)
print("Agent's response:", response.choices[0].message.content)
```
```javascript Node.js theme={"dark"}
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const response = await client.sessions.chat({
session_id: "SESSION_ID",
messages: [
{
role: "user",
content: "YOUR_MESSAGE"
}
]
});
console.log("Agent:", response.choices[0].message.content);
```
## Relationship to Other Concepts
This section will help you understand how sessions relate to other concepts in Julep.
### Agents
Agents operate within sessions to provide personalized and context-aware interactions. While an agent defines the behavior and capabilities, a session maintains the state and context of interactions between the agent and the user. In other words, the history of a conversation is tied to a session, rather than an agent.
Example:
```python theme={"dark"}
agent = client.agents.create(
name="David",
about="A news reporter",
model="gpt-4o-mini",
instructions=["Keep your responses concise and to the point.", "If you don't know the answer, say 'I don't know'"],
metadata={"channel": "FOX News"},
)
session1 = client.sessions.create(agent=agent.id, user="user_id", situation="The user is interested in the latest news about the stock market.")
session2 = client.sessions.create(agent=agent.id, user="user_id", situation="The user is interested in political news in the United States.")
```
In this example, the agent David is used in two different sessions, each with a different situation. The agent's behavior and responses are tailored to the specific situation of each session, and the history of messages in `session1` and `session2` are separate.
### Users
When a user (or more) is added to a session, the session will be able to access information about the user such as `name`, and `about` in order to personalize the interaction. Check out the `system_template` to see how the user's info is being accessed.
This is how you can create a user and associate it with a session:
```python Python theme={"dark"}
client = Julep(api_key="YOUR_API_KEY")
user = client.users.create(name="John Doe", about="A 21 year old man who is a student at MIT.")
agent = client.agents.create(name="Mark Lee", about="A 49 year old man who is a retired software engineer.")
session = client.sessions.create(agent_id=agent.id, user_id=user.id)
```
```javascript Node.js theme={"dark"}
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const user = await client.users.create({
name: "John Doe",
about: "A 21 year old man who is a student at MIT."
});
const agent = await client.agents.create({
name: "Mark Lee",
about: "A 49 year old man who is a retired software engineer."
});
const session = await client.sessions.create({
agent_id: agent.id,
user_id: user.id
});
```
In this example, the user John Doe is associated with the agent Mark Lee in the session. The session will use the user's information to personalize the interaction, such as using the user's name in the system prompt.
### Tools
Sessions have the ability to use [Tools](/concepts/tools). When `auto_run_tools` is set to `true` (available in chat calls), if an agent has a tool and the LLM decides to use it, the tool will be executed automatically and the result will be sent back to the LLM for further processing. When `auto_run_tools` is `false` (default), tool calls are returned in the response without execution.
Example:
If the agent that's associated with the session has a tool called `fetch_weather`, and the LLM decides to use it:
* With `auto_run_tools=true`: The tool executes automatically and returns weather data to the LLM
* With `auto_run_tools=false`: The tool call is returned in the response for manual execution
```python Python theme={"dark"}
# With automatic tool execution
response = client.sessions.chat(
session_id="session_id",
messages=[
{
"role": "user",
"content": "What is the weather in San Francisco?"
}
],
auto_run_tools=True, # Tools execute automatically
recall_tools=True # Tool calls/results included in message history
)
print("Agent's response:", response.choices[0].message.content)
# Agent's response: The weather in San Francisco is 70 degrees and it's sunny. Humidity is 50%, and the wind speed is around 10 mph.
# Without automatic tool execution (default)
response = client.sessions.chat(
session_id="session_id",
messages=[
{
"role": "user",
"content": "What is the weather in San Francisco?"
}
],
auto_run_tools=False # Default - returns tool calls without execution
)
# Check if tool calls were made
if response.choices[0].message.tool_calls:
print("Tool calls:", response.choices[0].message.tool_calls)
# You need to execute these tools manually and send results back
```
```javascript Node.js theme={"dark"}
// With automatic tool execution
const response = await client.sessions.chat({
session_id: "session_id",
messages: [
{
role: "user",
content: "What is the weather in San Francisco?"
}
],
auto_run_tools: true, // Tools execute automatically
recall_tools: true // Tool calls/results included in message history
});
console.log("Agent:", response.choices[0].message.content);
// Agent's response: The weather in San Francisco is 70 degrees and it's sunny. Humidity is 50%, and the wind speed is around 10 mph.
// Without automatic tool execution (default)
const response2 = await client.sessions.chat({
session_id: "session_id",
messages: [
{
role: "user",
content: "What is the weather in San Francisco?"
}
],
auto_run_tools: false // Default - returns tool calls without execution
});
// Check if tool calls were made
if (response2.choices[0].message.tool_calls) {
console.log("Tool calls:", response2.choices[0].message.tool_calls);
// You need to execute these tools manually and send results back
}
```
### Documents
When chatting in a session, the session can automatically search for documents that are associated with any of the agents and/or users that participate in the session. You can control whether the session should search for documents when chatting using the `recall` option of the `chat` method, which is set to `True` by default. You can also set the session's `recall_options` when creating the session to control how the session should search for documents.
```python Python [expandable] theme={"dark"}
# Create a session with custom recall options
client.sessions.create(
agent=agent.id,
user=user.id,
recall=True,
recall_options={
"mode": "vector", # or "hybrid", "text"
"num_search_messages": 4, # number of messages to search for documents
"max_query_length": 1000, # maximum query length
"alpha": 0.7, # weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
"confidence": 0.6, # confidence cutoff level (ranges from -1 to 1)
"limit": 10, # limit of documents to return
"lang": "en-US", # language to be used for text-only search
"metadata_filter": {}, # metadata filter to apply to the search
"mmr_strength": 0, # MMR Strength (ranges from 0 to 1)
}
)
# Chat in the session
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Tell me about Julep"
}
],
recall=True
)
print("Agent's response:", response.choices[0].message.content)
print("Searched Documents:", response.docs)
```
```javascript Node.js [expandable] theme={"dark"}
client.sessions.create({
agent: agent.id,
user: user.id,
recall: true,
recall_options: {
mode: "vector", // or "hybrid", "text"
num_search_messages: 4, // number of messages to search for documents
max_query_length: 1000, // maximum query length
alpha: 0.7, // weight to apply to BM25 vs Vector search results (ranges from 0 to 1)
confidence: 0.6, // confidence cutoff level (ranges from -1 to 1)
limit: 10, // limit of documents to return
lang: "en-US", // language to be used for text-only search
metadata_filter: {}, // metadata filter to apply to the search
mmr_strength: 0, // MMR Strength (ranges from 0 to 1)
}
});
// Chat in the session
const response = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "Tell me about Julep"
}
],
recall: true
});
```
When running the above code with an agent that has documents about Julep, the session will search for documents that are relevant to the conversation and return them in the `response.docs` field.
```[expandable] theme={"dark"}
Agent's response: Julep is a comprehensive platform designed for creating production-ready AI systems and agents. Here are the key aspects of Julep:
Core Features:
1. Complete Infrastructure Layer
- Provides infrastructure between LLMs and software
- Built-in support for long-term memory
- Multi-step process management
- State management capabilities
2. AI Agent Development
- Creates persistent AI agents that remember past interactions
- Supports complex task execution
- Enables multi-step workflows
- Includes built-in tools and integrations
3. Production-Ready Features
- Automatic retries for failed steps
- Message resending capabilities
- Task recovery systems
- Real-time monitoring
- Error handling
- Automatic scaling and load balancing
4. Development Approach
- Uses 8-Factor Agent methodology
- Treats prompts as code with proper versioning
- Provides clear tool interfaces
- Offers model independence to avoid vendor lock-in
- Includes structured reasoning capabilities
- Maintains ground truth examples for validation
Available Resources:
- Documentation: https://docs.julep.ai/
- API Playground: https://dev.julep.ai/api/docs
- Python SDK: https://github.com/julep-ai/python-sdk/blob/main/README.md
- JavaScript SDK: https://github.com/julep-ai/node-sdk/blob/main/README.md
- Various use case examples and cookbooks
You can explore different use cases through their cookbooks, including:
- User Profiling
- Email Assistant
- Trip Planner
- Document Management
- Website Crawler
- Multi-step Tasks
- Advanced Chat Interactions
For additional support or to learn more:
- Discord Community: https://discord.gg/2EUJzJU2Yt
- Book a Demo: https://calendly.com/ishita-julep
- Dev Support: hey@julep.ai
Searched Documents:
### Build faster using Julep API
### Deploy multi-step workflows easily with
### built-in tools and
### State management that is
### production ready from day one Keep me informed
### Build faster using Julep API
### Deploy multi-step workflows easily with built-in tools and State management that is production ready from day one Keep me informed
Keep me informed
Dev Resources
[Docs](https://docs.julep.ai/)
[Get API Key](https://dashboard-dev.julep.ai/)
[API Playground](https://dev.julep.ai/api/docs)
[Python SDK](https://github.com/julep-ai/python-sdk/blob/main/README.md)
[Javascript SDK](https://github.com/julep-ai/node-sdk/blob/main/README.md)
[Integration List](https://github.com/julep-ai/julep/tree/dev?tab=readme-ov-file#integrations)
Usecases
[User Profiling](https://github.com/julep-ai/julep/blob/dev/cookbooks/09-User_Management_and_Personalization.py)
[Email Assistant (Mailgun)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/00-Devfest-Email-Assistant.ipynb)
[Trip Planner (Weather + Wiki)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/04-TripPlanner_With_Weather_And_WikiInfo.ipynb)
[Doc Management](https://github.com/julep-ai/julep/blob/dev/cookbooks/10-Document_Management_and_Search.py)
[Website Crawler (Spider)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/01-Website_Crawler_using_Spider.ipynb)
[Multi-step Tasks](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-Designing_Multi-Step_Tasks.py)
[Advanced Chat](https://github.com/julep-ai/julep/blob/dev/cookbooks/11-Advanced_Chat_Interactions.py)
Company
[About](./about)
[Contact Us](./contact)
[Book Demo](https://calendly.com/ishita-julep)
Socials
[Github](https://github.com/julep-ai/julep)
[LinkedIn](https://www.linkedin.com/company/julep-ai)
[Twitter](https://x.com/julep_ai)
[Dev.to](https://dev.to/julep)
[Hugging Face](https://huggingface.co/julep-ai)
[Youtube](https://www.youtube.com/@julep_ai)
Built by Engineers, for Engineers
©Julep AI Inc 2024
Dev Resources
[Docs](https://docs.julep.ai/)
[Get API Key](https://dashboard-dev.julep.ai/)
[API Playground](https://dev.julep.ai/api/docs)
[Python SDK](https://github.com/julep-ai/python-sdk/blob/main/README.md)
[Javascript SDK](https://github.com/julep-ai/node-sdk/blob/main/README.md)
[Integration List](https://github.com/julep-ai/julep/tree/dev?tab=readme-ov-file#integrations)
Usecases
[User Profiling](https://github.com/julep-ai/julep/blob/dev/cookbooks/09-User_Management_and_Personalization.py)
[Email Assistant (Mailgun)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/00-Devfest-Email-Assistant.ipynb)
[Trip Planner (Weather + Wiki)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/04-TripPlanner_With_Weather_And_WikiInfo.ipynb)
[Doc Management](https://github.com/julep-ai/julep/blob/dev/cookbooks/10-Document_Management_and_Search.py)
[Website Crawler (Spider)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/01-Website_Crawler_using_Spider.ipynb)
[Multi-step Tasks](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-Designing_Multi-Step_Tasks.py)
[Advanced Chat](https://github.com/julep-ai/julep/blob/dev/cookbooks/11-Advanced_Chat_Interactions.py)
Company
[About](./about)
[Contact Us](./contact)
[Book Demo](https://calendly.com/ishita-julep)
Socials
[Github](https://github.com/julep-ai/julep)
[LinkedIn](https://www.linkedin.com/company/julep-ai)
[Twitter](https://x.com/julep_ai)
[Dev.to](https://dev.to/julep)
[Hugging Face](https://huggingface.co/julep-ai)
[Youtube](https://www.youtube.com/@julep_ai)
Built by Engineers, for Engineers
©Julep AI Inc 2024
Dev Resources
[Docs](https://docs.julep.ai/)
[Get API Key](https://dashboard-dev.julep.ai/)
[API Playground](https://dev.julep.ai/api/docs)
[Python SDK](https://github.com/julep-ai/python-sdk/blob/main/README.md)
[Javascript SDK](https://github.com/julep-ai/node-sdk/blob/main/README.md)
[Integration List](https://github.com/julep-ai/julep/tree/dev?tab=readme-ov-file#integrations)
Usecases
[User Profiling](https://github.com/julep-ai/julep/blob/dev/cookbooks/09-User_Management_and_Personalization.py)
[Email Assistant (Mailgun)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/00-Devfest-Email-Assistant.ipynb)
[Trip Planner (Weather + Wiki)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/04-TripPlanner_With_Weather_And_WikiInfo.ipynb)
[Doc Management](https://github.com/julep-ai/julep/blob/dev/cookbooks/10-Document_Management_and_Search.py)
[Website Crawler (Spider)](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/01-Website_Crawler_using_Spider.ipynb)
[Multi-step Tasks](https://github.com/julep-ai/julep/blob/dev/cookbooks/06-Designing_Multi-Step_Tasks.py)
[Advanced Chat](https://github.com/julep-ai/julep/blob/dev/cookbooks/11-Advanced_Chat_Interactions.py)
Company
[About](./about)
[Contact Us](./contact)
[Book Demo](https://calendly.com/ishita-julep)
Socials
[Github](https://github.com/julep-ai/julep)
[LinkedIn](https://www.linkedin.com/company/julep-ai)
[Twitter](https://x.com/julep_ai)
[Dev.to](https://dev.to/julep)
[Hugging Face](https://huggingface.co/julep-ai)
[Youtube](https://www.youtube.com/@julep_ai)
Built by Engineers, for Engineers
©Julep AI Inc 2024
This chunk provides an overview of resources and support options offered by Julep AI, including documentation, development tools, community links, and contact information for booking demos or reaching out for support. It highlights features such as the Julep API, multi-step workflow deployment, and various use cases relevant for developers.
```
This example is taken from the `crawling-and-rag` cookbook. Check it out [here](https://github.com/julep-ai/julep/blob/dev/cookbooks/advanced/10-crawling-and-rag.ipynb).
## Best Practices
- **1. Reuse Sessions**: Reuse existing sessions for returning users to maintain continuity in interactions.
- **2. Session Cleanup**: Regularly clean up inactive sessions to manage resources efficiently.
- **3. Context Overflow Strategy**: Choose an appropriate context overflow strategy (e.g., "adaptive") to handle long conversations without losing important information.
- **1. Leverage Metadata**: Use session metadata to store and retrieve user preferences, enhancing personalized interactions.
- **2. Maintain Context**: Ensure that the context within sessions is updated and relevant to provide coherent and context-aware responses.
- **1. Efficient Searches**: Optimize search queries within sessions to retrieve relevant documents quickly.
- **2. Manage Token Usage**: Monitor and manage token usage to ensure efficient use of resources, especially in long sessions.
## Next Steps
* [Agent Tools](/concepts/tools) - Learn about tools and how to use them with agents
* [Agent Tasks](/concepts/tasks) - Learn about tasks and how to use them with agents
* [Agent Docs](/concepts/docs) - Learn about docs and how to use them with agents
# Tasks
Source: https://docs.julep.ai/concepts/tasks
Understanding Julep Tasks and workflows
## Overview
Tasks are GitHub Actions-style workflows that define multi-step actions in Julep. Think of them as recipes that tell an agent exactly how to accomplish a goal. For example, a task might outline the steps to "Summarize a Research Paper" or "Debug a Code Issue".
Here are some of the key features of tasks:
* Connect multiple AI operations seamlessly
* Make decisions based on intermediate results
* Run operations in parallel for efficiency
* Integrate with external tools and APIs
* Maintain state throughout execution
## Components
A task consists of several key components which can be broadly classified into:
### Input Schema
```yaml YAML theme={"dark"}
name: Summarize Document
description: Create a concise summary of any document
input_schema:
type: object
properties:
document_text:
type: string
description: The text to summarize
```
**⚠️ Workflow Input Size Warning**: Avoid passing extremely large objects as input to your workflows, as this can cause execution failures. Large inputs (such as massive JSON objects, extensive arrays, or huge text files) may exceed memory limits or hit serialization constraints.
**What to do instead:**
* **Break large data into smaller chunks** and process them iteratively
* **Use file uploads** and pass file references instead of raw content
* **Implement pagination** for large datasets
**Rule of thumb:** If your input data is larger than a few megabytes, consider alternative approaches.
### Tools
Tools are functions that can be used by an agent to perform tasks. Julep supports:
* [User-defined functions](/concepts/tools#user-defined-functions)
* [System tools](/concepts/tools#system-tools)
* [Integrations](/concepts/tools#integration-tools)
* [API calls](/concepts/tools#api-call-tools)
Learn more about tools [here](/concepts/tools).
```yaml YAML theme={"dark"}
tools:
- name: internet_search
description: Performs an internet search using Brave
type: integration
integration:
provider: brave
method: search
setup:
brave_api_key:
```
### Sub-Workflows
A task can be made up of multiple sub-workflows. These sub-workflows can be named and can be used to break down complex tasks into smaller, more manageable pieces.
```yaml YAML theme={"dark"}
name: Summarize Document
description: Create a concise summary of any document
sample_sub_workflow:
- prompt: |-
$ f'Tell me a joke about {steps[0].input.topic}:'
main:
- workflow: sample_sub_workflow
arguments:
topic: AI
```
You can learn more about sub-workflows [here](/advanced/types-of-task-steps#subworkflow-step).
### Steps
We use tasks and workflows interchangeably. They are the same except Julep's branding reflects tasks.
Below is a table of all the steps that can be used in a task.
| Name | Description |
| ------------------------------------------------------------------- | -------------------------------------------- |
| [Tool Call](/advanced/types-of-task-steps#tool-call-step) | Execute tools defined in the task |
| [Prompt](/advanced/types-of-task-steps#prompt-step) | Send messages to the AI model |
| [Evaluate](/advanced/types-of-task-steps#evaluate-step) | Perform calculations or data manipulation |
| [Wait for Input](/advanced/types-of-task-steps#wait-for-input-step) | Pause workflow for user input |
| [Subworkflow](/advanced/types-of-task-steps#subworkflow-step) | Execute a subworkflow |
| [Set](/advanced/types-of-task-steps#set-step) | Store values for later use |
| [Get](/advanced/types-of-task-steps#get-step) | Retrieve values from storage |
| [Foreach](/advanced/types-of-task-steps#foreach-step) | Iterate over a collection |
| [Map-reduce](/advanced/types-of-task-steps#map-reduce-step) | Process collections in parallel |
| [Switch](/advanced/types-of-task-steps#switch-step) | Multiple condition handling |
| [If-else](/advanced/types-of-task-steps#if-else-step) | Conditional execution |
| [Sleep](/advanced/types-of-task-steps#sleep-step) | Pause execution |
| [Return](/advanced/types-of-task-steps#return-step) | Return values from workflow |
| [Yield](/advanced/types-of-task-steps#yield-step) | Execute subworkflows |
| [Log](/advanced/types-of-task-steps#log-step) | Log messages or specific values |
| [Error](/advanced/types-of-task-steps#error-step) | Handle errors by specifying an error message |
> You can learn more about workflow steps as to how they work in the [Workflow Steps](/advanced/types-of-task-steps) section.
### Context Variables
Tasks have access to three types of context:
#### Input Variables
Access input parameters:
```yaml YAML theme={"dark"}
- prompt: $ f'Hello {steps[0].input.user_name}'
```
#### Step Results
Use outputs from previous steps:
```yaml YAML theme={"dark"}
- evaluate: $ len(_.search_results)
- if: $ _.count > 0
```
In any step, you can access the input and output of a step using the `steps[index].output` or `steps[index].input` variable. For example:
```yaml theme={"dark"}
- evaluate:
topic: $ steps[0].input.topic
```
```yaml theme={"dark"}
- evaluate:
topic: $ steps[0].output.topic
```
In Julep, the steps are indexed from 0. So the first step is `steps[0]` and the second step is `steps[1]` and so on.
Furthermore the first step input is nothing but the task input and the last step output is nothing but the output of the task.
To learn more about how to use the `$` variable and new syntax, please refer to the [New Syntax](/advanced/new-syntax) section.
#### Environment Context
Access agent and session data:
```yaml YAML theme={"dark"}
- prompt: $ f'Agent {agent.name} is helping you'
```
Input schemas help catch errors early by validating all inputs before execution starts.
Here's how these components work together:
```yaml YAML [expandable] theme={"dark"}
name: Process Customer Feedback
description: Analyze and categorize customer feedback
input_schema:
type: object
required: ["feedback_text"]
properties:
feedback_text:
type: string
sentiment_analysis:
type: boolean
default: true
tools:
- name: get_weather_info
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: OPENWEATHERMAP_API_KEY
main:
- tool: get_weather_info
arguments:
location: $ steps[0].input.location
- prompt: |-
$ f"""The weather in {steps[0].output.location} is the following:
{steps[0].output.weather}
Analyze the weather and provide a summary of the weather in the location. Include some recommendations on what to wear based on the weather.
"""
```
> Learn more about tools [here](/concepts/tools).
### Metadata
Metadata is a key-value pair that can be used to categorize and filter tasks.
## How to Use Tasks ?
### Creating a Task
Here's a simple task that summarizes a document and checks if the summary is too long. We first define the task in a YAML file and then create it using the Julep SDK.
```yaml task.yaml [expandable] theme={"dark"}
name: Summarize Document
description: Create a concise summary of any document
input_schema:
type: object
properties:
document_text:
type: string
description: The text to summarize
main:
- prompt: |-
$ f'''Analyze the following text and create a summary:
{steps[0].input.document_text}'''
unwrap: true
- evaluate:
too_long: $ len(_) > 500
- if: $ _.too_long
then:
prompt: |-
$ f'''Make the summary more concise:
{steps[0].output}'''
unwrap: true
else:
evaluate:
content: $ steps[0].output
```
```python main.py theme={"dark"}
import yaml
import os
task_def = yaml.safe_load(open("task.yaml"))
task = client.tasks.create(
agent_id="agent_id",
**task_def
)
```
```javascript index.js theme={"dark"}
// Create a task
import yaml from "yaml";
import fs from "fs";
const task_definition = yaml.parse(fs.readFileSync("task.yaml", "utf8"));
async function createTask(agentId) {
const task = await client.tasks.create(agentId, task_definition);
return task;
}
```
Check out the API reference [here](/api-reference/tasks) or SDK reference (Python [here](/sdks/python/reference#tasks) or JavaScript [here](/sdks/nodejs/reference#tasks) for more details on different operations you can perform on tasks.
### Executing a Task
Here's how to execute a task:
```python Python theme={"dark"}
# Execute a task
execution = client.executions.create(
task_id=task.id,
input={
"document_text": "This is a sample document"
}
)
# Monitor progress
while True:
result = client.executions.get(execution.id)
if result.status in ["succeeded", "failed"]:
break
time.sleep(1)
```
```javascript Node.js [expandable] theme={"dark"}
// Execute a task
async function executeTask(taskId) {
const execution = await client.executions.create(taskId, {
input: { idea: "A cat who learns to fly" },
});
while (true) {
const result = await client.executions.get(execution.id);
console.log(result.status, result.output);
if (result.status === "succeeded" || result.status === "failed") {
// 📦 Once the execution is finished, retrieve the results
if (result.status === "succeeded") {
console.log(result.output);
} else {
throw new Error(result.error);
}
break;
}
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
```
Check out the API reference [here](/api-reference/executions) or SDK reference (Python [here](/sdks/python/reference#executions) or JavaScript [here](/sdks/nodejs/reference#executions) for more details on different operations you can perform on tasks.
## Relationship to Other Concepts
This section will help you understand how tasks relate to other concepts in Julep.
### Agents
Julep agents can power tasks by providing memory, context, or tools. Tasks are multi-step workflows designed for complex, automated execution. Whenever you create a task, you can associate it with an agent if you want to leverage that agent's capabilities. Unlike sessions, tasks are not meant for real-time interaction; they run through a defined workflow to achieve a goal.
For example:
```python Python [expandable] theme={"dark"}
import yaml
# Create an agent
agent = client.agents.create(
name="Customer Support Agent",
about="An agent that handles customer support requests",
model="gpt-4o",
)
# Add a tool to the agent
client.agents.tools.create(
agent_id=agent.id,
**yaml.safe_load("""
name: send_email
type: integration
integration:
provider: email
method: send
setup:
host: "smtp.example.com"
port: 587
user: "your_username"
password: "your_password"
"""),
)
# Create a task that inherits this tool
task = client.tasks.create(
agent_id=agent.id,
**yaml.safe_load("""
name: Handle Support Request
# Make sure to set this to true if you want to inherit tools from the agent
inherit_tools: true
input_schema:
type: object
properties:
customer_email:
type: string
description: The email of the customer
subject:
type: string
body:
type: string
description: The body of the email
main:
- prompt:
- role: system
content: You are a customer support agent who works for Julep AI. You will be given a support request from a customer. You will need to handle the request by sending a reply email to the customer.
- role: user
content: |-
$ f"""Handle the support request from this email: {steps[0].input.customer_email}
The subject of the email is this:
{steps[0].input.subject}
---
The body of the email is this:
{steps[0].input.body}
"""
unwrap: true
- tool: send_email
arguments:
to: $ steps[0].input.customer_email
from: "support@julep.ai"
subject: "$ f'Re: {steps[0].input.subject}'"
body: $ steps[0].output
""")
)
```
```javascript Node.js [expandable] theme={"dark"}
// Create an agent
const agent = await client.agents.create({
name: "Customer Support Agent",
about: "An agent that handles customer support requests",
model: "gpt-4o",
});
// Add a tool to the agent
await client.agents.tools.create(
agent.id,
yaml.parse(`
name: send_email
type: integration
integration:
provider: email
method: send
setup:
host: "smtp.example.com"
port: 587
user: "your_username"
password: "your_password"
`)
);
// Create a task that inherits this tool
const task = await client.tasks.create(
agent.id,
yaml.parse(`
name: Handle Support Request
# Make sure to set this to true if you want to inherit tools from the agent
inherit_tools: true
input_schema:
type: object
properties:
customer_email:
type: string
description: The email of the customer
subject:
type: string
body:
type: string
description: The body of the email
main:
- prompt:
- role: system
content: You are a customer support agent who works for Julep AI. You will be given a support request from a customer. You will need to handle the request by sending a reply email to the customer.
- role: user
content: |-
$ f"""Handle the support request from this email: {steps[0].input.customer_email}
The subject of the email is this:
{steps[0].input.subject}
---
The body of the email is this:
{steps[0].input.body}
"""
unwrap: true
- tool: send_email
arguments:
to: $ steps[0].input.customer_email
from: "support@julep.ai"
subject: "$ f'Re: {steps[0].input.subject}'"
body: $ steps[0].output
`)
);
```
### Tools
Task can leverage tools to perform complex operations. There are 2 ways of defining tools for tasks:
1. Associate a tool with an agent, and inherit it in the task definition by setting `inherit_tools` to `true` while creating the task. Example:
```python Python [expandable] theme={"dark"}
client.agents.tools.create(
agent_id="agent_id",
**yaml.safe_load("""
name: get_weather_info
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "your_openweathermap_api_key"
""")
task = client.tasks.create(
agent_id="agent_id",
**yaml.safe_load("""
name: Get Weather Info
inherit_tools: true
main:
- tool: get_weather_info
arguments:
location: New York
""")
)
```
```javascript Node.js [expandable] theme={"dark"}
await client.agents.tools.create(
agent.id,
yaml.parse(`
name: get_weather_info
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "your_openweathermap_api_key"
`)
);
task = await client.tasks.create(
agent.id,
yaml.parse(`
name: Get Weather Info
inherit_tools: true
main:
- tool: get_weather_info
arguments:
location: New York
`)
);
```
2. Define a tool in the task definition. Example:
```python Python theme={"dark"}
task = client.tasks.create(
agent_id="agent_id",
**yaml.safe_load("""
name: Get Weather Info
tools:
- name: get_weather_info
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "your_openweathermap_api_key"
main:
- tool: get_weather_info
arguments:
location: New York
""")
)
```
```javascript Node.js theme={"dark"}
task = await client.tasks.create(
agent.id,
yaml.parse(`
name: Get Weather Info
tools:
- name: get_weather_info
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "your_openweathermap_api_key"
main:
- tool: get_weather_info
arguments:
location: New York
`)
);
```
When you define a tool in the task definition, it is available to all steps in that task only. On the other hand, when you associate a tool with an agent, it is available to all the Tasks associated with that agent.
## Best Practices
- 1. **Purpose**: Each task should have a single, clear purpose
- 2. **Subtasks**: Break complex workflows into smaller subtasks
- 1. **Error Handling**: Use try/catch blocks for error-prone operations
- 2. **Error Messages**: Provide helpful error messages
- 3. **Fallback Options**: Include fallback options where appropriate
- 1. **Parallel Execution**: Use parallel execution when steps are independent
- 2. **Map-Reduce**: Use map-reduce to run steps in parallel
## Next Steps
* [Workflow Steps](/advanced/types-of-task-steps) - Learn about all available step types
* [Tools](/concepts/tools) - Learn about tools and how to use them in tasks
* [Sessions](/concepts/sessions) - Learn about sessions and how to use them in tasks
## See Examples
* [Simple Task notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/basics/02-Simple-Task.ipynb)
* [Trip Planning Assistant notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/03-trip-planning-assistant.ipynb)
# Tools
Source: https://docs.julep.ai/concepts/tools
Understanding tools in Julep
## Overview
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.
## Components
Tools in Julep consist of three main components:
1. **Name**: A unique identifier for the tool.
2. **Type**: The category of the tool. In Julep, there are four types of tools:
* **User-defined `functions`**: Function signatures provided to the model, similar to OpenAI's function-calling. These require client handling, and the workflow pauses until the client executes the function and returns the results to Julep. [Learn more](#user-defined-function-tool)
* **`system` tools**: Built-in tools for calling Julep APIs, such as triggering task execution or appending to a metadata field. [Learn more](#system-tool)
* **`integrations`**: Built-in third-party tools that enhance the capabilities of your agents. [Learn more](#integration-tool)
* **`api_calls`**: Direct API calls executed during workflow processes as tool calls. [Learn more](#api-call-tool)
3. **Arguments**: The inputs required by the tool.
#### System Tool
Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. See the [task execution system tool](#task-execution-system-tool) for a complete example.
`System` tools are built into the backend. They get executed automatically when needed. They do not require any action from the client-side. For example,
```yaml YAML theme={"dark"}
name: Example system tool task
description: List agents using system call
tools:
- name: list_agent_docs
description: List all docs for the given agent
type: system
system:
resource: agent
subresource: doc
operation: list
main:
- tool: list_agents
arguments:
limit: $ 10
```
- list: List all agents.
- get: Get a single agent by id.
- create: Create a new agent.
- update: Update an existing agent.
- delete: Delete an existing agent.
- list: List all users.
- get: Get a single user by id.
- create: Create a new user.
- update: Update an existing user.
- delete: Delete an existing user.
- list: List all sessions.
- get: Get a single session by id.
- create: Create a new session.
- update: Update an existing session.
- delete: Delete an existing session.
- chat: Chat with a session.
- history: Get the chat history with a session.
- list: List all tasks.
- get: Get a single task by id.
- create: Create a new task.
- update: Update an existing task.
- delete: Delete an existing task.
- execution.create: Start an execution for a task (see guidance below).
- list: List all documents.
- create: Create a new document.
- delete: Delete an existing document.
- search: Search for documents.
- embed: Embed a resource (specific resources not specified in the provided code).
- change\_status: Change the status of a resource (specific resources not specified in the provided code).
- chat: Chat with a resource (specific resources not specified in the provided code).
- history: Get the chat history with a resource (specific resources not specified in the provided code).
- create\_or\_update: Create a new resource or update an existing one (specific resources not specified in the provided code).
##### Task Execution system tool
Use the `execution.create` task tool when you want an agent run to immediately kick off another task execution. A minimal definition looks like this:
```yaml YAML theme={"dark"}
tools:
- name: create_task_execution
description: Launch a new execution for the current (or another) task
type: system
system:
resource: task
subresource: execution
operation: create
main:
- tool: create_task_execution
arguments:
task_id: $ steps[0].input.sub_task_id
data:
input: {}
```
`data.input` must satisfy the task's input schema. Use an empty object only when the schema allows it; otherwise populate the fields the task expects.
Operational notes:
* **Re-use existing tasks**: The `task_id` only has to belong to the same developer. You can start executions for tasks attached to different agents you own without extra setup.
* **Inspect prior runs**: To review execution history, call the REST endpoint `GET /tasks/{task_id}/executions` from a workflow via an `api_call` tool or use the SDK (`client.executions.list(task_id=...)`). That gives you the execution IDs needed to fetch outputs later (e.g. via transitions or `execution.get`).
#### Integration Tool
Julep comes with a number of built-in integrations (as described in the section below). `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` `metadata` fields.
An example of how to create a `integration` tool for an agent using the `wikipedia` integration:
```yaml YAML theme={"dark"}
name: Example integration tool task
description: Search wikipedia for a query
tools:
- name: wikipedia_search
description: Search wikipedia for a query
type: integration
integration:
provider: wikipedia
main:
- tool: wikipedia_search
arguments:
query: "Julep"
```
Checkout the list of integrations that Julep supports [here](/integrations/supported-integrations).
#### API Call Tool
Julep can also directly make `api_call` during workflow executions as tool calls. Similar to `integration` tools, additional runtime parameters are loaded from metadata fields.
API call tools support `params_schema` to define the expected parameters for the API call, enabling better validation and documentation:
```yaml YAML theme={"dark"}
name: Example api_call task
tools:
- type: api_call
name: weather_api
description: Get weather information for a location
api_call:
method: GET # Required - HTTP method must be specified
url: https://api.openweathermap.org/data/2.5/weather # Required - URL must be specified
params_schema: # Define expected parameters
type: object
properties:
q:
type: string
description: City name or coordinates
units:
type: string
enum: [metric, imperial]
description: Temperature units
appid:
type: string
description: API key (loaded from metadata)
required: [q, appid]
main:
- tool: weather_api
arguments:
params:
q: $ _.location # City from input
units: "metric"
appid: $ metadata.weather_api_key # API key from metadata
```
The `params_schema` follows JSON Schema format and helps:
* Document expected parameters for the API
* Enable LLMs to understand what parameters the tool accepts
* Provide validation for API calls
* Generate better tool descriptions for the model
- GET: Get a resource.
- POST: POST a resource.
- PUT: PUT a resource.
- DELETE: DELETE a resource.
- PATCH: PATCH a resource.
- HEAD: HEAD a resource.
- OPTIONS: OPTIONS a resource.
- CONNECT: CONNECT to a resource.
- TRACE: TRACE a resource.
- method: (Required) The HTTP method to use.
- url: (Required) The URL to call.
- params\_schema: JSON Schema definition for API parameters, enabling validation and better LLM understanding.
- schema: The schema of the response.
- headers: The headers to send with the request.
- content: The content as base64 to send with the request.
- data: The data to send as form data.
- files: The data to send as files data.
- json: The JSON body to send with the request.
- cookies: The cookies to send with the request.
- params: The parameters to send with the request.
- follow\_redirects: Follow redirects.
- timeout: The timeout for the request.
* Arguments provided during the tool call can override the default `method` and `url` set in the `api_call` configuration.
* We utlize the python httpx library to make the api calls. Please refer to the [httpx documentation](https://www.python-httpx.org/api/#request-parameters) for more details on the parameters.
#### User-defined Function Tool
These are function signatures that you can give the model to choose from, similar to how \[openai]'s function-calling works. An example:
```yaml YAML [expandable] theme={"dark"}
name: Example system tool task
description: List agents using system call
tools:
- name: send_notification
description: Send a notification to the user
type: function
function:
parameters:
type: object
properties:
text:
type: string
description: Content of the notification
main:
- tool: send_notification
arguments:
content: "hi"
```
Whenever julep encounters a user-defined function, it pauses, giving control back to the client and waits for the client to run the function call and give the results back to julep.
##### How User-Defined Functions Work
**User-defined function tools** in Julep work exactly like OpenAI's function calling:
1. **Definition**: You define a function signature with name, description, and parameters schema
2. **Model Decision**: The LLM decides when to call your function based on the conversation context
3. **Execution Pause**: When Julep encounters a user-defined function call, it pauses execution and returns control to your client
4. **Your Implementation**: You implement the actual function logic in your client code
5. **Result Return**: You send the function results back to Julep to continue the workflow
Unlike `integration`, `system`, or `api_call` tools that execute automatically, function tools require manual handling by your client application.
##### Complete Example: Math Calculator with Function Tools
Here's a comprehensive example showing how to implement and handle user-defined functions:
```python [expandable] theme={"dark"}
import yaml
import time
from julep import Julep
# Replace with your API key
API_KEY = "your-api-key"
client = Julep(api_key=API_KEY, environment="production")
# 1. Define your actual functions
def add_numbers(a, b):
"""Add two numbers together"""
result = a + b
print(f"Adding {a} + {b} = {result}")
return result
def multiply_numbers(x, y):
"""Multiply two numbers"""
result = x * y
print(f"Multiplying {x} × {y} = {result}")
return result
# Function dispatcher
FUNCTION_MAP = {
"add_numbers": add_numbers,
"multiply_numbers": multiply_numbers,
}
def execute_function(function_name, arguments):
"""Execute a function by name with arguments"""
if function_name not in FUNCTION_MAP:
raise ValueError(f"Unknown function: {function_name}")
func = FUNCTION_MAP[function_name]
return func(**arguments)
# 2. Create agent
agent = client.agents.create(
name="Math Calculator",
model="gpt-4o-mini",
instructions="You are a calculator."
)
# 3. Define task with inline tools
task_def = yaml.safe_load("""
name: "function-tool-demo"
tools:
- name: add_numbers
type: function
function:
parameters:
type: object
properties:
a:
type: number
description: First number
b:
type: number
description: Second number
required: ["a", "b"]
main:
- tool: add_numbers
arguments:
a: 15
b: 27
""")
# 4. Create and execute task
task = client.tasks.create(agent_id=agent.id, **task_def)
execution = client.executions.create(task_id=task.id, input={})
print(f"Execution: {execution.id}")
# 5. Monitor and handle function calls
for i in range(15):
execution = client.executions.get(execution.id)
print(f"Iteration {i+1}: Status = {execution.status}")
if execution.status == "awaiting_input":
# Execute the function based on task definition
tool_name = task_def["main"][0]["tool"] # "add_numbers"
arguments = task_def["main"][0]["arguments"] # {"a": 15, "b": 27}
result = execute_function(tool_name, arguments)
print(f"Result: {result}")
# Use change_status to provide tool results
client.executions.change_status(
execution_id=execution.id,
status="running", # Resume execution
input={"result": result}
)
print("Sent result back to Julep")
elif execution.status == "succeeded":
print(f"Output: {execution.output}")
break
elif execution.status == "failed":
print(f"Failed: {execution.error}")
break
time.sleep(3)
```
##### Key Points
1. **Execution Status Monitoring**: You must monitor the execution status and watch for `awaiting_input`
2. **Manual Function Execution**: When status is `awaiting_input`, execute your function locally
3. **Resume with Results**: Use `change_status` to resume execution with the function output
4. **Alternative Tool Creation**: You can also use `client.agents.tools.create()` to create tools before running the task
## How to Use Tools
### Create a Tool for an Agent
A tool can be created for an agent using the `client.agents.tools.create` method.
An example of how to create a `integration` tool for an agent using the `wikipedia` integration:
```python Python theme={"dark"}
# Create an agent
agent = client.agents.create(name="My Agent")
# Associate a tool with the agent
client.agents.tools.create(
agent_id=AGENT_UUID,
**{
"name": "wikipedia_search",
"type": "integration",
"integration": {
"provider": "wikipedia",
}
},
)
```
```javascript Node.js theme={"dark"}
async function createAgent() {
const agent = await client.agents.create({
name: "My Agent",
});
const tool = await client.agents.tools.create(agent.id, {
name: "wikipedia_search",
type: "integration",
integration: {
provider: "wikipedia",
},
});
return agent;
}
```
Check out the API reference [here](api-reference/agents/tools/create) or SDK reference (Python [here](/sdks/python/reference#Tools) or JavaScript [here](/sdks/nodejs/reference#Tools) for more details on different operations you can perform on agents.
### Execute a Tool for a Task
To create a tool for a task, you can use the `client.tasks.create` method and define the tool in that `task` definitions.
```yaml YAML theme={"dark"}
name: Example integration tool task
description: Search wikipedia for a query
tools:
- name: wikipedia_search
description: Search wikipedia for a query
type: integration
integration:
provider: wikipedia
main:
- tool: wikipedia_search
arguments:
query: "Julep"
```
```python Python theme={"dark"}
# Create a task
import yaml
task_yaml = """
// ... task yaml here ...
"""
task_def = yaml.safe_load(task_yaml)
task = client.tasks.create(
agent_id="agent_id",
**task_def
)
execution = client.executions.create(
task_id=task.id,
input={
"document_text": "This is a sample document"
}
)
```
```javascript Node.js theme={"dark"}
// Create a task
const taskYaml = `
// ... task yaml here ...
`
async function createTask(agentId) {
const task = await client.tasks.create(agentId, yaml.parse(taskYaml));
return task;
}
// Execute a task
async function executeTask(taskId) {
const execution = await client.executions.create(taskId, {
input: {
document_text: "This is a sample document"
}
});
return execution;
}
```
Check out the API reference [here](api-reference/agents/tools/create) or SDK reference (Python [here](/sdks/python/reference#Tools) or JavaScript [here](/sdks/nodejs/reference#Tools) for more details on different operations you can perform on agents.
## Relationship to Other Concepts
This section will help you understand how tools relate to other concepts in Julep.
### Task
When a tool is associated with a task, it is meant to be used only for that task. It is not associated with other tasks. An agent associated with that task will have access to that tool, but the same agent associated with another task will not have that access. This ensures that tools are used in a context-specific manner, providing precise functionality tailored to the task's requirements.
### Agent
When a tool is associated with an agent, it is meant to be used across all tasks associated with that agent. This allows for greater flexibility and reuse of tools, as the agent can leverage the same tool in multiple tasks. It also simplifies the management of tools, as they only need to be defined once for the agent and can then be utilized in various tasks.
## Automatic Tool Execution
Julep supports automatic tool execution, allowing agents to seamlessly use tools without manual intervention. This feature is controlled by the `auto_run_tools` parameter.
### How Automatic Tool Execution Works
1. **In Sessions (Chat)**:
* Set `auto_run_tools=true` when calling `sessions.chat()`
* When the model decides to use a tool, Julep automatically:
* Executes the tool with the provided arguments
* Captures the tool's output
* Sends the results back to the model
* Continues the conversation with the tool results
* All happens in a single API call - no manual intervention needed
2. **In Tasks (Prompt Steps)**:
* Set `auto_run_tools: true` in the prompt step definition
* During task execution, tools are automatically invoked when needed
* Results flow seamlessly into subsequent steps
3. **Default Behavior** (`auto_run_tools=false`):
* Tool calls are returned in the response
* Your application must handle tool execution
* Results must be sent back in a follow-up message
### Example: Session with Automatic Tools
```python Python theme={"dark"}
# Create agent with multiple tools
agent = client.agents.create(
name="Research Assistant",
tools=[
{
"name": "web_search",
"type": "integration",
"integration": {"provider": "brave"}
},
{
"name": "wikipedia",
"type": "integration",
"integration": {"provider": "wikipedia"}
}
]
)
# Chat with automatic tool execution
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Tell me about the latest developments in quantum computing"
}
],
auto_run_tools=True # Enable automatic execution
)
# Response includes information gathered from tools
print(response.choices[0].message.content)
# "Based on recent search results, here are the latest developments..."
```
```javascript Node.js theme={"dark"}
// Create agent with multiple tools
const agent = await client.agents.create({
name: "Research Assistant",
tools: [
{
name: "web_search",
type: "integration",
integration: {provider: "brave"}
},
{
name: "wikipedia",
type: "integration",
integration: {provider: "wikipedia"}
}
]
});
// Chat with automatic tool execution
const response = await client.sessions.chat({
session_id: session.id,
messages: [
{
role: "user",
content: "Tell me about the latest developments in quantum computing"
}
],
auto_run_tools: true // Enable automatic execution
});
// Response includes information gathered from tools
console.log(response.choices[0].message.content);
// "Based on recent search results, here are the latest developments..."
```
### Example: Task with Automatic Tools
```yaml YAML theme={"dark"}
name: Research and Summarize Task
tools:
- name: web_search
type: integration
integration:
provider: brave
- name: save_research_summary
description: Save the research summary as a document
type: system
system:
resource: agent
subresource: doc
operation: create
main:
# Step 1: Search with automatic tool execution
- prompt:
- role: user
content: "Search for recent news about {{topic}} and create a comprehensive summary and save it as a document for agent {{agent.id}}"
auto_run_tools: true # Tools execute automatically
# Step 2: The results from tools are already available
- evaluate:
summary: $ _.choices[0].message.content
```
### When to Use Automatic Tool Execution
**Use `auto_run_tools=true` when**:
* Building conversational agents that need real-time information
* Creating autonomous workflows in tasks
* You want a seamless, single-call interaction
* Tools are trusted and don't require manual validation
**Use `auto_run_tools=false` when**:
* You need to validate or modify tool inputs before execution
* Tool execution requires user confirmation
* You want to handle tool errors with custom logic
* Building applications with complex tool orchestration
**Important**: User-defined function tools (`type: function`) are **never** executed automatically, regardless of the `auto_run_tools` setting. They always pause execution and return control to the client for manual handling. This is a key difference from `system`, `integration`, and `api_call` tools.
### Tool Execution Flow
The LLM analyzes the user's request and decides if a tool is needed
The model generates appropriate tool calls with arguments
With auto\_run\_tools=true: Julep executes the tool automatically
With auto\_run\_tools=false: Tool calls are returned for manual handling
Tool results are fed back to the model or returned to the client
The model uses tool results to generate the final response
### Managing Tool History
When using `sessions.chat()`, the `recall_tools` parameter controls whether tool interactions are saved in the conversation history:
* `recall_tools=true` (default): Tool calls and results are preserved
* `recall_tools=false`: Tool interactions are excluded from history
This helps maintain clean conversation logs while still benefiting from tool capabilities.
## Best Practices
- **1. Naming Conventions**: Use clear and consistent naming conventions for tools to make them easily identifiable and understandable.
- **1. Correct Usage**: Ensure that tools are used correctly and in the appropriate context. This includes providing the necessary arguments and ensuring that the tools are executed as intended.
- **1. Type**: Ensure that the type is correct for the tool you are using. Checkout the Tool Types Definitions here for further details.
## Next Steps
* [Checkout the Integration](/integrations/supported-integrations) - Learn how to use executions in an integration
* [Checkout the Tutorial](/tutorials/trip-planning) - Learn how to use tools in a task
## See Examples
* [Adding Tools notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/basics/03-Adding-Tools.ipynb)
* [Browser Use notebook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/06-browser-use.ipynb)
# Users
Source: https://docs.julep.ai/concepts/users
Understanding and Managing Users in Julep
## Overview
Users in Julep represent the entities interacting with your AI agents. These can be real people or other systems that require automated responses from an agent. Managing users effectively allows for personalized and contextual interactions, ensuring that each user's preferences and history are maintained across sessions.
## Components
Users are comprised of several key components that facilitate their interaction with agents and sessions:
* **Name**: The identifier for the user.
* **About**: Additional information describing the user.
* **Metadata**: Customizable key-value pairs that store user-specific data, preferences, and other relevant information.
### Users configuration options
When creating a user, the following attributes can be specified:
| **Field** | **Type** | **Description** | **Default** |
| ---------- | -------- | ------------------------------------------------------------------ | ----------- |
| `name` | `string` | The name of the user. | Required |
| `project` | `string` | The canonical name of the project this user belongs to. | `"default"` |
| `about` | `string` | Information about the user. | `""` |
| `metadata` | `object` | Additional metadata for the user, such as preferences or settings. | `null` |
## Creating a User
You can create a user using Julep's SDKs in Python or JavaScript. Below are examples demonstrating how to create a user.
```python Python theme={"dark"}
from julep import Julep
client = Julep(api_key="your_api_key")
user = client.users.create(
name="John Doe",
project="university-assistant",
about="A 21-year-old man who is a student at MIT.",
metadata={
"email": "john.doe@example.com",
"preferences": {
"language": "en",
"timezone": "UTC"
}
}
)
print(f"Created user: {user.id}")
```
```javascript Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
const client = new Julep({ apiKey: 'your_api_key' });
const user = await client.users.create({
name: "John Doe",
project: "university-assistant",
about: "A 21-year-old man who is a student at MIT.",
metadata: {
email: "john.doe@example.com",
preferences: {
language: "en",
timezone: "UTC"
}
}
});
console.log(`Created user: ${user.id}`);
```
## Managing Users
Once users are created, you can perform various operations such as retrieving, updating, and deleting user profiles.
### Retrieving Users
Retrieve user information individually or as a list.
```python Python theme={"dark"}
# Get a user by ID
user = client.users.get(user_id="user_id_here")
print(user)
# List all users
users = client.users.list()
for user in users:
print(user)
```
```javascript Node.js theme={"dark"}
// Get a user by ID
const user = await client.users.get("user_id_here");
console.log(user);
// List all users
const users = await client.users.list();
users.forEach(user => console.log(user));
```
Check out the [API reference](/api-reference/users) or SDK reference ([Python](/sdks/python/reference#users) or [JavaScript](/sdks/nodejs/reference#users)) for more details on different operations you can perform on users.
### Updating Users
Update user details or specific fields within the user profile.
```python Python theme={"dark"}
# Update user metadata
updated_user = client.users.update(
user_id="user_id_here",
metadata={
"preferences": {
"language": "es",
"notifications_enabled": True
}
}
)
print(f"Updated user: {updated_user.id}")
```
```javascript Node.js theme={"dark"}
// Update user metadata
const updatedUser = await client.users.update("user_id_here", {
metadata: {
preferences: {
language: "es",
notifications_enabled: true
}
}
});
console.log(`Updated user: ${updatedUser.id}`);
```
Check out the [API reference](/api-reference/users) or SDK reference ([Python](/sdks/python/reference#users) or [JavaScript](/sdks/nodejs/reference#users)) for more details on different operations you can perform on users.
### Deleting Users
Remove users from your system when they are no longer needed.
```python Python theme={"dark"}
# Delete a single user
client.users.delete(user_id="user_id_here")
```
```javascript Node.js theme={"dark"}
// Delete a single user
await client.users.delete("user_id_here");
```
Check out the API reference [here](/api-reference/users) or SDK reference (Python [here](/sdks/python/reference#users) or JavaScript [here](/sdks/nodejs/reference#users) for more details on different operations you can perform on users.
## Relationship to Other Concepts
### Projects
Users belong to exactly one project, which helps organize related resources together. When creating a user, you can specify which project it belongs to using the `project` parameter. If not specified, the user will be assigned to the "default" project.
**Example:**
```python Python theme={"dark"}
# Create a user in a specific project
user = client.users.create(
name="Marketing User",
project="marketing-campaign"
)
```
```javascript Node.js theme={"dark"}
// Create a user in a specific project
const user = await client.users.create({
name: "Marketing User",
project: "marketing-campaign"
});
```
For more information about projects, see [Projects](/concepts/projects).
### Sessions
Users interact with agents through **sessions**, which maintain the context and history of conversations. Each session is associated with a single user and an agent, ensuring that interactions are personalized and relevant.
**Example:**
```python Python theme={"dark"}
client = Julep(api_key="YOUR_API_KEY")
user = client.users.create(name="Alice", about="An avid reader.")
agent = client.agents.create(name="BookBot", about="Helps find and recommend books.")
session = client.sessions.create(agent=agent.id, user=user.id)
```
```javascript Node.js theme={"dark"}
const client = new Julep({ apiKey: 'YOUR_API_KEY' });
const user = await client.users.create({
name: "Alice",
about: "An avid reader."
});
const agent = await client.agents.create({
name: "BookBot",
about: "Helps find and recommend books."
});
const session = await client.sessions.create({
agent: agent.id,
user: user.id
});
```
### Agents
While users represent the entities interacting with your system, **agents** are the AI-powered entities that respond to user interactions. Agents can have access to user data to provide personalized and context-aware responses.
**Example:**
```python Python theme={"dark"}
agent = client.agents.create(
name="SupportBot",
about="Assists customers with their inquiries.",
model="gpt-4"
)
user = client.users.create(name="Bob", about="A customer with recent purchases.")
session = client.sessions.create(agent=agent.id, user=user.id)
```
```javascript Node.js theme={"dark"}
const agent = await client.agents.create({
name: "SupportBot",
about: "Assists customers with their inquiries.",
model: "gpt-4"
});
const user = await client.users.create({
name: "Bob",
about: "A customer with recent purchases."
});
const session = await client.sessions.create({
agent: agent.id,
user: user.id
});
```
Check out the [API reference](/api-reference/agents) or SDK reference ([Python](/sdks/python/reference#agents) or [JavaScript](/sdks/nodejs/reference#agents)) for more details on different operations you can perform on agents.
## Best Practices
- 1. **Reuse Sessions**: Reuse existing sessions for returning users to maintain continuity in interactions.
- 2. **Session Cleanup**: Regularly clean up inactive sessions to manage resources efficiently.
- 3. **Context Overflow Strategy**: Choose an appropriate context overflow strategy (e.g., "adaptive") to handle long conversations without losing important information.
- 1. **Leverage Metadata**: Use user metadata to store and retrieve preferences, enhancing personalized interactions.
- 2. **Maintain Context**: Ensure that the context within sessions is updated and relevant to provide coherent and context-aware responses.
- 1. **Protect User Data**: Ensure that all user data, especially sensitive information, is stored securely and complies with relevant data protection regulations.
- 2. **Access Control**: Implement proper access controls to restrict who can view or modify user data.
## Next Steps
* [Sessions](/concepts/sessions) - Learn how to manage sessions and maintain conversation context.
* [Agents](/concepts/agents) - Learn how to manage agents and create personalized interactions.
* [Tasks](/concepts/tasks) - Learn how to manage tasks and create automated workflows.
# Adding a Tool Integration
Source: https://docs.julep.ai/guides/adding-tool-integration
Extend Julep with your own tool or API
# Adding a Tool Integration
This guide explains how to connect a new external tool or API to Julep. You will create a small service that wraps the tool and exposes actions that agents can call.
## 1. Create an Integration Service
Use the Julep SDK to define your integration service. Each action should accept structured inputs and return structured outputs.
## 2. Register the Service
Deploy the service and register its OpenAPI specification with your Julep project. The new actions become available to your agents.
## 3. Invoke From a Task
Call the actions from your task steps. The agent can now leverage your custom tool as part of its workflow.
# Integration Patterns
Source: https://docs.julep.ai/guides/advanced/integration-patterns
Common patterns for integrating with external services
# Integration Patterns
When integrating external services with Julep, following consistent patterns helps ensure security, reliability, and maintainability. This guide covers common integration patterns with a focus on using secrets effectively.
## Authentication Patterns
### API Key Authentication with Secrets
For services that use API keys for authentication, store them as secrets:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: external_api
operation: fetch_data
arguments:
url: "https://api.example.com/data"
headers:
Authorization: "$ f'Bearer {secrets.api_key}'"
X-API-Key: "$ secrets.api_key"
```
### OAuth Authentication with Secrets
For OAuth flows, keep client credentials in secrets:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: oauth_service
operation: get_token
arguments:
client_id: "$ secrets.oauth_client_id"
client_secret: "$ secrets.oauth_client_secret"
scope: "read write"
output: token
- kind: tool_call
tool: api_service
operation: call_api
arguments:
url: "https://api.example.com/data"
headers:
Authorization: "Bearer {{ token }}"
```
### Basic Authentication with Secrets
For services using basic authentication:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: api_service
operation: call_api
arguments:
url: "https://api.example.com/data"
auth:
username: "$ secrets.api_username"
password: "$ secrets.api_password"
```
## Integration Configuration Patterns
### Database Connection with Secrets
When connecting to databases, use secrets for connection parameters:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: database
operation: query
arguments:
query: "SELECT * FROM users LIMIT 10"
connection:
host: "$ secrets.db_host"
port: "$ secrets.db_port"
user: "$ secrets.db_username"
password: "$ secrets.db_password"
database: "$ secrets.db_name"
```
### Service Configuration with Secrets
For configuring service endpoints and parameters:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: email
operation: send
arguments:
to: "recipient@example.com"
subject: "Important update"
body: "This is an important message."
smtp:
host: "$ secrets.smtp_host"
port: "$ secrets.smtp_port"
username: "$ secrets.smtp_username"
password: "$ secrets.smtp_password"
tls: true
```
## Advanced Integration Patterns
### Hybrid Secret and Expression Pattern
Combine secrets with expressions for dynamic configurations:
```yaml theme={"dark"}
steps:
- kind: transform
expression: "$ f'https://{secrets.api_domain}/v1/{input.resource}?api_key={secrets.api_key}'"
input:
resource: "users"
output: api_url
- kind: tool_call
tool: http
operation: get
arguments:
url: "{{ api_url }}"
```
### Multi-tenant Service Integration
For handling multiple tenant configurations with secrets:
```yaml theme={"dark"}
steps:
- kind: transform
expression: "$ f'tenant_{input.tenant_id}'"
input:
tenant_id: "123"
output: tenant_key
- kind: transform
expression: "$ f'{secrets[tenant_key + \"_api_key\"]}'"
output: api_key
- kind: tool_call
tool: external_api
operation: fetch_data
arguments:
url: "https://api.example.com/data"
headers:
Authorization: "Bearer {{ api_key }}"
```
### Service Discovery Pattern
For dynamically selecting services based on configuration:
```yaml [expandable] theme={"dark"}
steps:
- kind: transform
expression: "$ secrets.preferred_service"
output: service_name
- kind: if_else
if: "$ service_name == 'service_a'"
then:
- kind: tool_call
tool: service_a
operation: process
arguments:
input: "{{ input }}"
api_key: "$ secrets.service_a_api_key"
else:
- kind: tool_call
tool: service_b
operation: process
arguments:
data: "{{ input }}"
auth_token: "$ secrets.service_b_auth_token"
```
## Best Practices
### Secret Naming Conventions
* Use descriptive names: `stripe_api_key` instead of just `api_key`
* Use service prefixes: `aws_access_key`, `aws_secret_key`
* For multiple environments: `dev_api_key`, `prod_api_key`
### Secret Rotation
Implement regular secret rotation without service disruption:
```python [expandable] theme={"dark"}
# Python example of rotating a secret
from julep import Julep
import uuid
client = Julep(api_key="your_api_key")
# Generate temporary name
temp_name = f"stripe_key_rotation_{uuid.uuid4().hex[:8]}"
# Create new secret with temp name
client.secrets.create(
name=temp_name,
value="sk_new_value...",
description="New Stripe API key (rotation)"
)
# Test the new key works
# ...
# If valid, delete old secret and rename new one
client.secrets.delete(name="stripe_api_key")
client.secrets.update(
name=temp_name,
new_name="stripe_api_key",
description="Stripe API key"
)
```
### Error Handling
For graceful handling of authentication and configuration errors:
```yaml [expandable] theme={"dark"}
steps:
- kind: try_catch
try:
- kind: tool_call
tool: external_api
operation: fetch_data
arguments:
url: "https://api.example.com/data"
headers:
Authorization: "Bearer $ secrets.api_key"
catch:
- kind: if_else
if: "$ error.type == 'AuthenticationError'"
then:
- kind: prompt
model: gpt-4
prompt: "API key authentication failed. Please suggest troubleshooting steps."
else:
- kind: prompt
model: gpt-4
prompt: "An error occurred: {{ error.message }}"
```
## Next Steps
* [Secrets Management](/advanced/secrets-management) - Learn about advanced secrets management
* [Complex Workflows](/guides/advanced/complex-workflows) - Build complex workflows with integrations
* [Multi-Agent Systems](/guides/advanced/multi-agent-systems) - Coordinate multiple agents with integrations
# Chat with an Agent
Source: https://docs.julep.ai/guides/getting-started/chat-with-an-agent
Learn how to chat with your agent
# How to Create a Julep Session and Chat with an Agent
This guide will walk you through the process of creating an agent, setting up a session, and engaging in a conversation using the Julep SDKs.
This guide is minimalistic and designed to quickly set you up to chat with the agent. It does not cover all the features that Julep agents and sessions provide. For more advanced usage, please check out the [Agents](/concepts/agents) and [Sessions](/concepts/sessions) concepts.
## Step 1: Initialize the Julep Client
First, you need to initialize the Julep client with your API key.
```python Python theme={"dark"}
from julep import Julep
# Initialize the Julep client
julep = Julep(api_key="your_api_key")
```
```javascript Node.js theme={"dark"}
const julep = require('@julep/sdk');
// Initialize the Julep client
const julep = new Julep({ apiKey: 'your_api_key' });
```
## Step 2: Create an Agent
Create an agent with specific `instructions` and a `model`. This agent will be used in the session.
```python Python theme={"dark"}
# Create an agent
agent = julep.agents.create(
name="Chat Buddy",
about="A friendly and helpful chatbot",
instructions=[
"Be friendly and engaging.",
"Be helpful and provide useful information.",
"Be concise and to the point.",
"Do not format your responses. Keep them as plain text.",
],
model="gpt-4o-mini",
)
```
```javascript Node.js theme={"dark"}
// Create an agent
const agent = await julep.agents.create({
name: "Chat Buddy",
about: "A friendly and helpful chatbot",
instructions: [
"Be friendly and engaging.",
"Be helpful and provide useful information.",
"Be concise and to the point.",
"Do not format your responses. Keep them as plain text.",
],
model: "gpt-4o-mini",
});
```
## Step 3: Create a Session
Create a session with the agent, specifying a `situation` to provide more context for the session.
```python Python theme={"dark"}
# Create a session
session = julep.sessions.create(
agent=agent.id,
situation="User wants to have a casual chat about hobbies.",
)
```
```javascript Node.js theme={"dark"}
// Create a session
const session = await julep.sessions.create({
agent: agent.id,
situation: "User wants to have a casual chat about hobbies.",
});
```
## Step 4: Chat with the Agent
Send a message with a `user` role to the session to trigger the agent to send a response.
```python Python theme={"dark"}
# Chat with the agent
response = julep.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Hi there! What are some fun hobbies to try out?"
}
]
)
print("Agent's Response:")
print(response.choices[0].message.content)
```
```javascript Node.js theme={"dark"}
// Chat with the agent
const response = await julep.sessions.chat(
sessionId: session.id,
messages: [
{
role: "user",
content: "Hi there! What are some fun hobbies to try out?"
}
]
)
```
## Full Example
```python Python [expandable] theme={"dark"}
from julep import Julep
# Initialize the Julep client
julep = Julep(api_key="your_api_key")
# Create an agent
agent = julep.agents.create(
name="Chat Buddy",
about="A friendly and helpful chatbot",
instructions=[
"Be friendly and engaging.",
"Be helpful and provide useful information.",
"Be concise and to the point.",
"Do not format your responses. Keep them as plain text.",
],
model="gpt-4o-mini",
)
# Create a session
session = julep.sessions.create(
agent=agent.id,
situation="User wants to have a casual chat about hobbies.",
)
# Chat with the agent
response = julep.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "Hi there! What are some fun hobbies to try out?"
}
]
)
```
```javascript Node.js [expandable] theme={"dark"}
const julep = require('@julep/sdk');
// Initialize the Julep client
const julep = new Julep({ apiKey: 'your_api_key' });
// Create an agent
const agent = await julep.agents.create({
name: "Chat Buddy",
about: "A friendly and helpful chatbot",
instructions: [
"Be friendly and engaging.",
"Be helpful and provide useful information.",
"Be concise and to the point.",
"Do not format your responses. Keep them as plain text.",
],
model: "gpt-4o-mini",
});
// Create a session
const session = await julep.sessions.create({
agent: agent.id,
situation: "User wants to have a casual chat about hobbies.",
});
// Chat with the agent
const response = await julep.sessions.chat(
sessionId: session.id,
messages: [
{
role: "user",
content: "Hi there! What are some fun hobbies to try out?"
}
]
)
```
## Conclusion
By following these steps, you can create an agent, set up a session, and quickly engage in a conversation using the Julep SDKs. This setup allows for personalized and context-aware interactions with the agent. For more advanced usage, don't forget to check out the [Agents](/concepts/agents) and [Sessions](/concepts/sessions) concepts.
# Create & Execute a Julep Task
Source: https://docs.julep.ai/guides/getting-started/create-and-execute-julep-task
Learn how to create and execute a Julep task
# Create and Execute a Julep Task
This guide will walk you through the process of creating a Julep task and executing it.
This guide is based on the Trip Planning task. For a detailed explanation of the task's workflow, please check out the corresponding [Trip Planning tutorial](/tutorials/trip-planning).
## Step 1: Initialize the Julep Client
First, you need to initialize the Julep client with your API key.
```python Python theme={"dark"}
from julep import Julep
# Initialize the Julep client
julep = Julep(api_key="your_api_key")
```
```javascript Node.js theme={"dark"}
const julep = require('@julep/sdk');
// Initialize the Julep client
const julep = new Julep({ apiKey: 'your_api_key' });
```
## Step 2: Create a Julep Agent
Create an agent to associate the task with. In Julep, tasks are scoped to agents, and agents take on the responsibility of executing tasks.
```python Python theme={"dark"}
# Create an agent
agent = julep.agents.create(
name="Task Agent",
model="gpt-4o"
)
```
```javascript Node.js theme={"dark"}
// Create an agent
const agent = await julep.agents.create({
name: "Task Agent",
model: "gpt-4o"
});
```
## Step 3: Create a Julep Task
In this step, you will define the task that the Julep agent will execute. This involves specifying the task's name, description, input schema, tools, and the main workflow. The task definition is written in YAML format and includes details about the integrations and the logic for processing the input data.
```python Python [expandable] theme={"dark"}
import yaml
task_def = yaml.safe_load("""
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
""")
# Create a task
task = julep.tasks.create(
agent_id=agent.id,
**task_def
)
```
```javascript Node.js [expandable] theme={"dark"}
const yaml = require("yaml");
const task_def = yaml.safeLoad("""
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
""")
// Create a task
const task = await julep.tasks.create({
name: "Trip Planning",
agentId: agent.id
});
```
# Step 4: Execute the Julep Task
Once the task is created, you can execute it by providing the necessary input that matches the task's input schema. This step involves calling the execute method on the task, which will start the task execution process.
```python Python theme={"dark"}
# Execute the task with specific input that matches the task's input schema
execution = julep.tasks.execute(
task_id=task.id,
input={"locations": ["New York", "Paris", "Tokyo"]}
)
```
```javascript Node.js theme={"dark"}
// Execute the task with specific input that matches the task's input schema
const execution = await julep.tasks.execute(
taskId: task.id,
input: {"locations": ["New York", "Paris", "Tokyo"]}
)
```
## Step 5: Get the Task Execution Result
After executing the task, you can retrieve the results by checking the execution status and output. This step involves polling the execution status until it reaches a terminal state (succeeded, failed, or cancelled) and printing the current output. Alternatively, you can fetch the execution transitions for a more detailed view of the task's progress, which is useful for debugging.
### Fetching the Execution Status & Current Output
```python Python theme={"dark"}
import time
# Fetch the execution status & current output
execution = julep.executions.get(execution_id=execution.id)
while execution.status not in ["succeeded", "failed", "cancelled"]:
execution = julep.executions.get(execution_id=execution.id)
print(f"Execution status: {execution.status}")
print(f"Execution output: {execution.output}")
print("************************************************")
# Wait for 5 seconds before polling again
time.sleep(5)
```
```javascript Node.js theme={"dark"}
// Fetch the execution status & current output
const execution = await julep.executions.get(executionId=execution.id);
while (execution.status !== "succeeded" && execution.status !== "failed" && execution.status !== "cancelled") {
execution = await julep.executions.get(executionId=execution.id);
console.log(`Execution status: ${execution.status}`);
console.log(`Execution output: ${execution.output}`);
console.log("************************************************");
// Wait for 5 seconds before polling again
await new Promise(resolve => setTimeout(resolve, 5000));
}
```
### Fetching the Execution Transitions
```python Python theme={"dark"}
import time
# Fetch the execution transitions
transitions = julep.executions.transitions.list(execution_id=execution.id)
# Wait until the execution is either finished, errored, or canceled
while transitions[0].items.type not in ["finish", "error", "canceled"]:
transitions = julep.executions.transitions.list(execution_id=execution.id)
# Transitions are ordered from the latest to the oldest
for transition in reversed(transitions.items):
print(f"Transition type: {transition.type}")
print(f"Transition output: {transition.output}")
print("************************************************")
# Wait for 5 seconds before fetching the next set of transitions
time.sleep(5)
```
```javascript Node.js theme={"dark"}
// Fetch the execution transitions
const transitions = await julep.executions.transitions.list(executionId=execution.id);
// Wait until the execution is either finished, errored, or canceled
while (transitions[0].items.type !== "finish" && transitions[0].items.type !== "error" && transitions[0].items.type !== "canceled") {
transitions = await julep.executions.transitions.list(executionId=execution.id);
for (const transition of transitions.items) {
console.log(`Transition type: ${transition.type}`);
console.log(`Transition output: ${transition.output}`);
console.log("************************************************");
}
// Wait for 5 seconds before fetching the next set of transitions
await new Promise(resolve => setTimeout(resolve, 5000));
}
```
## Full Example
```python Python [expandable] theme={"dark"}
from julep import Julep
import yaml
import time
# Initialize the Julep client
julep = Julep(api_key="your_api_key")
# Create an agent
agent = julep.agents.create(
name="Task Agent",
model="gpt-4o"
)
task_def = yaml.safe_load("""
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
""")
# Create a task
task = julep.tasks.create(
agent_id=agent.id,
**task_def
)
# Execute the task with specific input that matches the task's input schema
execution = julep.tasks.execute(
task_id=task.id,
input={"locations": ["New York", "Paris", "Tokyo"]}
)
# Fetch the execution status & current output
execution = julep.executions.get(execution_id=execution.id)
while execution.status not in ["succeeded", "failed", "cancelled"]:
execution = julep.executions.get(execution_id=execution.id)
print(f"Execution status: {execution.status}")
print(f"Execution output: {execution.output}")
print("************************************************")
# Wait for 5 seconds before polling again
time.sleep(5)
```
```javascript Node.js [expandable] theme={"dark"}
const julep = require('@julep/sdk');
// Initialize the Julep client
const julep = new Julep({ apiKey: 'your_api_key' });
// Create an agent
const agent = await julep.agents.create({
name: "Task Agent",
model: "gpt-4o"
});
const yaml = require("yaml");
const task_def = yaml.safeLoad("""
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
""")
// Create a task
const task = await julep.tasks.create({
name: "Trip Planning",
agentId: agent.id
});
// Execute the task with specific input that matches the task's input schema
const execution = await julep.tasks.execute(
taskId: task.id,
input: {"locations": ["New York", "Paris", "Tokyo"]}
)
// Fetch the execution status & current output
const execution = await julep.executions.get(executionId=execution.id);
while (execution.status !== "succeeded" && execution.status !== "failed" && execution.status !== "cancelled") {
execution = await julep.executions.get(executionId=execution.id);
console.log(`Execution status: ${execution.status}`);
console.log(`Execution output: ${execution.output}`);
console.log("************************************************");
// Wait for 5 seconds before polling again
await new Promise(resolve => setTimeout(resolve, 5000));
}
```
## Conclusion
This guide provided a comprehensive overview of how to create and execute a Julep task. It covered the necessary steps, including initializing the Julep client, creating an agent, defining the task, executing it, and retrieving the results. By following these steps, you can effectively use Julep's capabilities to automate complex workflows and achieve your goals.
## Next Steps
* [Tasks](/concepts/tasks) - Learn about Julep tasks in more details and see other tasks steps.
* [Tutorials](/tutorials) - Check out other tutorials to see more real-world examples of Julep in action.
* [Cookbooks](https://github.com/julep-ai/julep/tree/dev/cookbooks) - Easy to run Jupyter notebooks to execute Julep tasks.
# Modifying Agent Workflow
Source: https://docs.julep.ai/guides/modifying-agent-workflow
Customize how your agents process tasks
# Modifying Agent Workflow
Julep tasks are flexible workflows that control how an agent operates. This guide shows how to adjust the default behavior.
## 1. Edit the Task YAML
Add, remove, or reorder steps in your task definition. You can insert tool calls, conditional logic, or loops to suit your needs.
## 2. Update Agent Configuration
Point your agent to the new task file or update its task list via the SDK. Your changes apply immediately to new sessions.
## 3. Test Iteratively
Run the task locally or through the API to verify each modification. Adjust prompts and steps until the workflow produces the desired output.
# Using Secrets in Julep
Source: https://docs.julep.ai/guides/using-secrets
A practical guide to managing and using secrets in your Julep applications
# Using Secrets in Julep
This guide will walk you through the process of creating, managing, and using secrets in your Julep applications. Secrets provide a secure way to store and use sensitive information like API keys, credentials, and tokens without exposing them in your code or configuration files.
## Creating Secrets
You can create secrets using the Julep CLI, SDKs, or directly through the API.
### Using the CLI
```bash theme={"dark"}
# Create a new secret
julep secrets create --name "openai_api_key" --value "sk-..." --description "OpenAI API key for production"
# List all secrets
julep secrets list
# Get a specific secret
julep secrets get openai_api_key
```
### Using the Python SDK
```python theme={"dark"}
from julep import Julep
client = Julep(api_key="your_api_key")
# Create a secret
client.secrets.create(
name="openai_api_key",
value="sk-...",
description="OpenAI API key for production"
)
# List all secrets
secrets = client.secrets.list()
for secret in secrets.items:
print(f"{secret.name}: {secret.description}")
```
### Using the Node.js SDK
```javascript theme={"dark"}
import { Julep } from '@julep/sdk';
const julep = new Julep({ apiKey: 'your_api_key' });
// Create a secret
await julep.secrets.create({
name: 'openai_api_key',
value: 'sk-...',
description: 'OpenAI API key for production'
});
// List all secrets
const secrets = await julep.secrets.list();
secrets.items.forEach(secret => {
console.log(`${secret.name}: ${secret.description}`);
});
```
### Using the REST API
You can also create secrets directly using the REST API:
```bash theme={"dark"}
curl -X POST "https://api.julep.ai/v1/secrets/{developer_id}" \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"name": "openai_api_key",
"value": "sk-...",
"description": "OpenAI API key for production"
}'
```
## Using Secrets in Tasks
Once you've created secrets, you can reference them in your tasks using the `secret_name` field or the `secrets` object.
### Direct Secret Reference
For tools that require a single API key or token:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: openai
operation: chat
arguments:
model: "gpt-4"
messages:
- role: "user"
content: "What's the weather like in San Francisco?"
secret_name: openai_api_key
```
### Multiple Secrets
For tools that require multiple secrets:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: email
operation: send
arguments:
to: "recipient@example.com"
subject: "Hello from Julep"
body: "This is a test email sent from Julep."
secrets:
service_api_key: "email_service_api_key"
sender_address: "email_sender_address"
```
### Using Secrets in Expressions
You can reference secrets in expressions using the `secrets` object:
```yaml theme={"dark"}
steps:
- kind: transform
expression: "$ f'https://api.example.com/v1?api_key={secrets.api_key}&query={input}'"
input: "search query"
output: api_url
```
For template variables in prompts:
```yaml theme={"dark"}
steps:
- kind: prompt
model: gpt-4
prompt: "Access the database at {{ db_url }} with credentials {{ credentials }}"
template_variables:
db_url: "$ secrets.db_host"
credentials: "$ f'User: {secrets.db_username}, Password: {secrets.db_password}'"
```
## Managing Secrets
### Updating Secrets
To update an existing secret:
```bash theme={"dark"}
# CLI
julep secrets update openai_api_key --value "new-sk-..."
# Python SDK
client.secrets.update(
name="openai_api_key",
value="new-sk-..."
)
# Node.js SDK
await julep.secrets.update({
name: 'openai_api_key',
value: 'new-sk-...'
});
```
### Adding Metadata
You can add metadata to organize and categorize your secrets:
```python theme={"dark"}
client.secrets.create(
name="stripe_api_key",
value="sk_test_...",
description="Stripe API key for payment processing",
metadata={
"environment": "production",
"owner": "payments-team",
"rotation_date": "2025-05-10"
}
)
```
This metadata can be used for filtering when listing secrets:
```python theme={"dark"}
production_secrets = client.secrets.list(
metadata={"environment": "production"}
)
```
### Deleting Secrets
When a secret is no longer needed:
```bash theme={"dark"}
# CLI
julep secrets delete openai_api_key
# Python SDK
client.secrets.delete(name="openai_api_key")
# Node.js SDK
await julep.secrets.delete({ name: 'openai_api_key' });
```
## Common Use Cases
### Securing LLM API Keys
Julep can automatically use developer secrets for LLM API keys based on the provider:
```python theme={"dark"}
# Store the API key as a secret
client.secrets.create(
name="OPENAI_API_KEY",
value="sk-..."
)
# The key will be automatically used for OpenAI requests
task = client.tasks.create({
"steps": [
{
"kind": "prompt",
"model": "gpt-4",
"prompt": "Generate a story about space exploration."
}
]
})
```
### External API Integration
For tools that call external APIs:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: api
operation: request
arguments:
method: "GET"
url: "https://api.example.com/data"
headers:
Authorization: "$ f'Bearer {secrets.api_token}'"
X-API-Key: "$ secrets.api_key"
```
### Database Connections
For database operations:
```yaml theme={"dark"}
steps:
- kind: tool_call
tool: postgres
operation: query
arguments:
query: "SELECT * FROM users LIMIT 10"
connection:
host: "$ secrets.pg_host"
user: "$ secrets.pg_user"
password: "$ secrets.pg_password"
database: "$ secrets.pg_database"
```
## Best Practices
1. **Never commit secrets** to version control
2. Use descriptive names for your secrets
3. Add metadata to organize your secrets
4. Rotate secrets regularly
5. Use the minimum necessary permissions
6. Delete unused secrets promptly
7. Use secret references instead of hardcoding values
## Next Steps
* [Secrets Management](/advanced/secrets-management) - Advanced guide for managing secrets
* [Integration Patterns](/guides/advanced/integration-patterns) - Learn how to use secrets with integrations
* [API Reference](/api-reference#tag/secrets) - Complete API reference for secrets
# Email (SMTP)
Source: https://docs.julep.ai/integrations/communicationdata/email
Learn how to use the Email integration with Julep
## Overview
Welcome to the Email integration guide for Julep! This integration allows you to send emails using SMTP, enabling you to build workflows that require email communication capabilities. Whether you're sending notifications or managing email campaigns, this guide will walk you through the setup and usage.
## Prerequisites
To use the Email integration, you need SMTP server credentials, including the host, port, username, and password. Ensure you have access to an SMTP server before proceeding.
## How to Use the Integration
To get started with the Email integration, follow these steps to configure and create a task:
Add your SMTP server credentials to the tools section of your task. This will allow Julep to authenticate requests to your email server on your behalf.
Use the following YAML configuration to define your email sending task:
```yaml Email Example [expandable] theme={"dark"}
name: Email Task
tools:
- name: email_tool
type: integration
integration:
provider: email
method: send
setup:
host: "smtp.example.com"
port: 587
user: "YOUR_USERNAME"
password: "YOUR_PASSWORD"
main:
- tool: email_tool
arguments:
to: recipient@example.com # this is a placeholder for the actual recipient email
from: sender@example.com # this is a placeholder for the actual sender email
subject: Hello from Julep # this is a placeholder for the actual subject
body: This is a test email sent using Julep's Email integration. # this is a placeholder for the actual body
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Email Task".
* ***tools***: This section lists the tools or integrations being used. Here, `email_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `email` for Email integration.
* ***method***: Specifies the method to be used. Default is `send` if not specified. If not specified, the method will be `send` by default.
* ***setup***: Contains configuration details.
* ***host***: (Required) The SMTP server host.
* ***port***: (Required) The SMTP server port.
* ***user***: (Required) The SMTP server username.
* ***password***: (Required) The SMTP server password.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`email_tool`).
* ***arguments***: Specifies the input parameters for the tool:
* ***to***: The email address to send the email to.
* ***from***: The email address to send the email from.
* ***subject***: The subject of the email.
* ***body***: The body of the email.
* Please note that the `to` and `from` arguments can accept a single email address only.
* Replace the `YOUR_USERNAME` and `YOUR_PASSWORD` with your actual SMTP server credentials.
* Remember to replace the SMTP server credentials and email addresses with your actual information. Ensure your SMTP server allows sending emails from the specified addresses.
## Conclusion
With the Email integration, you can efficiently send emails using SMTP.
This integration provides a robust solution for email communication, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [SMTP documentation](https://docs.python.org/3/library/smtplib.html).
# Google Sheets
Source: https://docs.julep.ai/integrations/communicationdata/google-sheets
Learn how to use the Google Sheets integration with Julep
## Overview
Welcome to the Google Sheets integration guide for Julep! This integration allows you to read, write, and manage data in Google Sheets spreadsheets, enabling you to build workflows that leverage structured data storage and manipulation. Whether you're tracking metrics, managing inventories, or processing data tables, this guide will walk you through the setup and usage.
## Prerequisites
To use the Google Sheets integration, you need either:
1. A Google Cloud service account with Sheets API enabled (recommended)
2. Use Julep's shared service account (limited to spreadsheets shared with it)
For your own service account, follow the [Google Get Started guide](https://developers.google.com/workspace/guides/get-started) to create credentials.
## How to Use the Integration
To get started with the Google Sheets integration, follow these steps to configure and create a task:
Choose between using your own service account or Julep's shared service. For your own service account, base64 encode your JSON credentials file.
Use the following YAML configuration examples for different operations:
### Read Values Example
```yaml Read Values theme={"dark"}
name: Google Sheets Read Task
tools:
- name: sheets_reader
type: integration
integration:
provider: google_sheets
method: read_values
setup:
# Option 1: Use your own service account
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
# Option 2: Use Julep's service (comment out service_account_json)
# use_julep_service: true
main:
- tool: sheets_reader
arguments:
spreadsheet_id: "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
range: "Sheet1!A1:C10"
```
### Write Values Example
```yaml Write Values theme={"dark"}
name: Google Sheets Write Task
tools:
- name: sheets_writer
type: integration
integration:
provider: google_sheets
method: write_values
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
main:
- tool: sheets_writer
arguments:
spreadsheet_id: "YOUR_SPREADSHEET_ID"
range: "Sheet1!A1:B2"
values:
- ["Name", "Score"]
- ["Alice", 95]
```
### Append Values Example
```yaml Append Values theme={"dark"}
name: Google Sheets Append Task
tools:
- name: sheets_appender
type: integration
integration:
provider: google_sheets
method: append_values
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
main:
- tool: sheets_appender
arguments:
spreadsheet_id: "YOUR_SPREADSHEET_ID"
range: "Sheet1!A:B"
values:
- ["Bob", 87]
- ["Charlie", 92]
```
### Clear Values Example
```yaml Clear Values theme={"dark"}
name: Google Sheets Clear Task
tools:
- name: sheets_clearer
type: integration
integration:
provider: google_sheets
method: clear_values
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
main:
- tool: sheets_clearer
arguments:
spreadsheet_id: "YOUR_SPREADSHEET_ID"
range: "Sheet1!A2:B100"
```
### Batch Read Example
```yaml Batch Read theme={"dark"}
name: Google Sheets Batch Read Task
tools:
- name: sheets_batch_reader
type: integration
integration:
provider: google_sheets
method: batch_read
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
main:
- tool: sheets_batch_reader
arguments:
spreadsheet_id: "YOUR_SPREADSHEET_ID"
ranges:
- "Sheet1!A1:C5"
- "Sheet2!D1:F10"
- "Summary!A1:B20"
```
### Batch Write Example
```yaml Batch Write theme={"dark"}
name: Google Sheets Batch Write Task
tools:
- name: sheets_batch_writer
type: integration
integration:
provider: google_sheets
method: batch_write
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
main:
- tool: sheets_batch_writer
arguments:
spreadsheet_id: "YOUR_SPREADSHEET_ID"
data:
- range: "Sheet1!A1:B2"
values:
- ["Updated", "Data"]
- ["New", "Values"]
- range: "Sheet2!C1:D2"
values:
- ["More", "Updates"]
- ["Here", "Too"]
```
### YAML Explanation
* ***name***: A descriptive name for the task (e.g., "Google Sheets Read Task").
* ***tools***: This section lists the tools or integrations being used. Each tool has a unique name for reference.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Always `google_sheets` for Google Sheets integration.
* ***method***: The operation to perform. Available methods:
* `read_values`: Read data from a range
* `write_values`: Write or update data in a range
* `append_values`: Append new rows to a sheet
* `clear_values`: Clear data from a range
* `batch_read`: Read from multiple ranges at once
* `batch_write`: Write to multiple ranges at once
* ***setup***: Authentication configuration (see Authentication Methods below).
You have two options for authentication:
**Option 1: Your Own Service Account (Recommended)**
```yaml theme={"dark"}
setup:
service_account_json: "BASE64_ENCODED_SERVICE_ACCOUNT_JSON"
```
* Full control over permissions
**Option 2: Julep's Shared Service (Testing Only)**
```yaml theme={"dark"}
setup:
use_julep_service: true
```
* No setup required
* Limited to spreadsheets explicitly shared with Julep's service account
* **Recommended for testing only** - Use your own service account in production to manage Google API quotas and constraints
**Common Arguments:**
* ***spreadsheet\_id***: The ID of the Google Sheets spreadsheet (found in the URL)
* ***range***: The A1 notation range (e.g., "Sheet1!A1:C10")
**Method-Specific Arguments:**
* ***values*** (write/append): 2D array of data to write
* ***ranges*** (batch\_read): Array of ranges to read
* ***data*** (batch\_write): Array of range-value pairs to write
## Important Notes
* **Spreadsheet ID**: Found in the spreadsheet URL: `https://docs.google.com/spreadsheets/d/{SPREADSHEET_ID}/edit`
* **Range Notation**: Use A1 notation like "Sheet1!A1:C10" or "A:A" for entire columns
* **Service Account Setup**: Your service account needs the Google Sheets API enabled in the Google Cloud Console
* **Sharing Requirements**: When using `use_julep_service`, share your spreadsheet with Julep's service account email: `julep-sheets-assistant@julep-471013.iam.gserviceaccount.com`
* **Base64 Encoding**: Encode your service account JSON with: `base64 -i service-account.json`
## Conclusion
With the Google Sheets integration, you can efficiently manage spreadsheet data within your Julep workflows.
This integration provides robust data management capabilities, from simple reads and writes to complex batch operations, enhancing your workflow's ability to work with structured data.
For more information, please refer to:
* [Google Sheets API documentation](https://developers.google.com/sheets/api/reference/rest)
* [A1 notation guide](https://developers.google.com/sheets/api/guides/concepts#a1_notation)
* [Service account setup guide](https://cloud.google.com/iam/docs/service-accounts)
# OpenWeatherMap
Source: https://docs.julep.ai/integrations/communicationdata/weather
OpenWeatherMap integration with Julep
## Overview
Welcome to the OpenWeatherMap integration guide for Julep! This integration allows you to seamlessly access weather data for various locations, enabling you to build workflows that require real-time weather information.
## Prerequisites
To use the OpenWeatherMap integration, you need an API key. You can obtain this key by signing up at [OpenWeatherMap](https://home.openweathermap.org/users/sign_up).
## How to Use the Integration
To get started with the OpenWeatherMap integration, follow these steps to configure and create a task:
Add your API key to the tools section of your task. This will allow Julep to authenticate requests to OpenWeatherMap on your behalf.
Use the following YAML configuration to request weather data service in your task definition:
```yaml Weather Request Example theme={"dark"}
name: Weather Request
tools:
- name: weather_call
type: integration
integration:
provider: weather
method: get
setup:
openweathermap_api_key: "OPENWEATHERMAP_API_KEY"
main:
- tool: weather_call
arguments:
location: London
```
Deploy your task by creating a new execution.
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Weather Request".
* ***tools***: This section lists the tools or integrations being used. Here, `weather_call` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `weather` for OpenWeatherMap.
* ***method***: Specifies the method to be used. Default is `get` if not specified. If not specified, the method will be `get` by default.
* ***setup***: Contains configuration details, such as the API key (`openweathermap_api_key`) required for authentication.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`weather_call`).
* ***arguments***: Specifies the input parameters for the tool:
* ***location***: The location for which weather data is needed.
Remember to replace `OPENWEATHERMAP_API_KEY` with your actual API key.
## Conclusion
With the OpenWeatherMap integration, you can easily incorporate weather data into your workflows.
This integration provides a robust solution for accessing real-time weather information, enhancing your workflow's functionality and user experience.
For more information, please refer to the [OpenWeatherMap documentation](https://openweathermap.org/guide).
# Add New Integrations
Source: https://docs.julep.ai/integrations/contributing-integrations
Guidelines for contributing new integrations to Julep
## Overview
This guide provides instructions for contributing new integrations to the Julep platform. Follow these steps to ensure your integration is successfully added to the project.
## Steps to Contribute
1. **Add a New Integration Provider**:
* Add the provider name to the `integrationProvider` alias in `typespec/tools/models.tsp`.
* Create a new file in `typespec/tools/` with the provider name. Refer to existing files for examples.
2. **Generate OpenAPI Schema and Pydantic Models**:
* Run `./scripts/generate_openapi_code.sh` from the root directory to generate the necessary files.
3. **Implement Integration Logic**:
* Add the integration logic in `integration-service/integrations/utils/integrations`.
4. **Register the Provider**:
* Add the provider to the `available_providers` dictionary in `integrations-service/integrations/providers.py`.
## Additional Resources
* [CONTRIBUTING.md](https://github.com/julep-ai/julep/blob/dev/.github/CONTRIBUTING.md) for more detailed instructions.
# MCP (Model Context Protocol)
Source: https://docs.julep.ai/integrations/extensibility/mcp
Learn how to use the MCP integration to connect Julep with any MCP-compatible server
## Overview
Welcome to the MCP (Model Context Protocol) integration guide for Julep! This integration enables you to connect Julep agents with any MCP-compatible server, providing access to a vast ecosystem of tools and capabilities. MCP is a standardized protocol that allows language models to interact with external tools and services in a consistent, secure manner.
The MCP integration is unique because instead of hardcoding specific tools, it dynamically discovers available capabilities from any MCP server. This makes Julep infinitely extensible - simply point it at a new MCP server and all its tools become available to your agents automatically.
## Prerequisites
To use the MCP integration, you need access to an MCP-compatible server. The server can be:
* A public MCP server (e.g., DeepWiki, GitHub Copilot MCP)
* A private MCP server you've deployed
Some servers may require authentication tokens or API keys.
## Supported Transports
The MCP integration supports two transport types:
Standard request-response pattern for tool execution. Works with servers that expose HTTP endpoints.
Server-Sent Events for streaming responses and real-time updates. Ideal for long-running operations.
## How to Use the Integration
To get started with the MCP integration, you need to define two types of tools:
First, create a tool that discovers available capabilities from the MCP server using the `list_tools` method.
Then, create a tool that executes specific MCP tools using the `call_tool` method.
Use the discovered tools in your task workflow to interact with the MCP server.
## Examples
### Example 1: Basic HTTP Transport (DeepWiki)
```yaml theme={"dark"}
name: Test DeepWiki with HTTP Transport
tools:
- type: integration
name: mcp_fetch
integration:
provider: mcp
method: list_tools
setup:
transport: http
http_url: https://mcp.deepwiki.com/mcp
- type: integration
name: mcp_call_tool
integration:
provider: mcp
method: call_tool
setup:
transport: http
http_url: https://mcp.deepwiki.com/mcp
main:
- tool: mcp_fetch
- tool: mcp_call_tool
arguments:
tool_name: read_wiki_structure
arguments:
repoName: facebook/react
```
### Example 2: SSE Transport with Headers
```yaml theme={"dark"}
name: Test DeepWiki with SSE Transport
tools:
- type: integration
name: mcp_sse
integration:
provider: mcp
method: call_tool
setup:
transport: sse
http_url: https://mcp.deepwiki.com/sse
http_headers:
Accept: "text/event-stream"
Cache-Control: "no-cache"
- type: integration
name: mcp_fetch
integration:
provider: mcp
method: list_tools
setup:
transport: sse
http_url: https://mcp.deepwiki.com/sse
main:
- tool: mcp_fetch
- tool: mcp_sse
arguments:
tool_name: "read_wiki_structure"
arguments:
repoName: "julep-ai/julep"
```
### Example 3: Authenticated MCP Server (GitHub)
```yaml theme={"dark"}
name: Simple GitHub MCP Test (with authorization)
tools:
- type: integration
name: github_mcp
integration:
provider: mcp
method: list_tools
setup:
transport: http
http_url: https://api.githubcopilot.com/mcp/
http_headers:
Authorization: "Bearer {your_github_token}"
Accept: "application/json, text/event-stream"
Content-Type: "application/json"
- type: integration
name: github_call
integration:
provider: mcp
method: call_tool
setup:
transport: http
http_url: https://api.githubcopilot.com/mcp/
http_headers:
Authorization: "Bearer {your_github_token}"
Accept: "application/json, text/event-stream"
Content-Type: "application/json"
main:
- tool: github_mcp
- tool: github_call
arguments:
tool_name: "search_repositories"
arguments:
query: "julep language:python"
perPage: 3
minimal_output: true
```
## YAML Configuration Explained
* ***name***: A descriptive name for the task
* ***tools***: Lists the MCP integration tools being used
* ***type***: Must be `integration` for MCP tools
* ***provider***: Must be `mcp` for MCP integration
* ***method***: Either `list_tools` or `call_tool`
* `list_tools`: Discovers available tools from the MCP server
* `call_tool`: Executes a specific tool on the MCP server
* ***setup***: Connection configuration
* ***transport***: Either `http` or `sse`
* ***http\_url***: The MCP server endpoint URL
* ***http\_headers***: (Optional) HTTP headers for authentication or content negotiation
For `call_tool` method:
* ***tool\_name***: The name of the MCP tool to execute
* ***arguments***: (Optional) Arguments to pass to the MCP tool
* ***timeout\_seconds***: (Optional) Per-call timeout in seconds (default: 60)
For `list_tools` method:
* No arguments required (empty object or omit entirely)
## Best Practices
**Tool Discovery**: Always call `list_tools` first to discover available capabilities before attempting to use specific tools. This ensures you're aware of what tools are available and their required parameters.
**Authentication**: Never hardcode authentication tokens in your task definitions. Use Julep's secrets management to store sensitive credentials securely.
* Different MCP servers expose different tools. Always check the server's documentation for available capabilities
* SSE transport is recommended for long-running operations or when you need real-time updates
* HTTP transport is simpler and works well for quick request-response operations
* Some servers may have rate limits - consider implementing retry logic in your tasks
## Advanced Features
### Dynamic Tool Discovery
The MCP integration's killer feature is dynamic tool discovery. Instead of defining tools statically, your agents can:
1. Connect to any MCP server
2. Discover available tools at runtime
3. Adapt their capabilities based on what's available
This means you can:
* Switch between different MCP servers without changing your code
* Add new capabilities by simply deploying new MCP servers
* Build agents that adapt to their environment
### Response Handling
MCP tool responses are normalized into a consistent format:
```json theme={"dark"}
{
"text": "Concatenated text content if any",
"structured": {
// Any structured data returned by the tool
},
"content": [
// Raw content items as returned by the server
],
"is_error": false
}
```
This allows you to handle responses consistently regardless of the underlying MCP server implementation.
## Using MCP with Automatic Tool Execution
One of the most powerful features of Julep is automatic tool execution, which works seamlessly with MCP integrations. This allows your agents to dynamically discover and use MCP tools without manual intervention.
### How It Works
When you combine MCP integration with Julep's `auto_run_tools` feature:
1. **Tool Discovery**: The agent first calls `list_tools` to discover available capabilities from the MCP server
2. **Automatic Execution**: When the model determines an MCP tool is needed, it's executed automatically
3. **Result Integration**: Tool results are fed back to the model to continue processing
4. **Seamless Workflow**: Everything happens in a single call - no manual intervention required
### Example: Autonomous MCP Agent in Tasks
```yaml theme={"dark"}
name: Autonomous Documentation Assistant
tools:
- type: integration
name: mcp_discover
integration:
provider: mcp
method: list_tools
setup:
transport: http
http_url: https://mcp.deepwiki.com/mcp
- type: integration
name: mcp_execute
integration:
provider: mcp
method: call_tool
setup:
transport: http
http_url: https://mcp.deepwiki.com/mcp
main:
# Step 1: Use discovered tools automatically to answer questions
- prompt:
- role: user
content: |
Using the available MCP tools, find information about the React repository structure
and provide a comprehensive overview of its main components.
auto_run_tools: true # MCP tools execute automatically when needed
```
## Troubleshooting
* Verify the MCP server URL is correct and accessible
* Check if authentication headers are required and properly formatted
* Ensure the transport type matches what the server expects
* Use `list_tools` to verify the tool exists on the server
* Check the tool's input schema for required parameters
* Ensure Bearer tokens include the "Bearer " prefix
* Verify API keys/tokens are valid and not expired
* Check if additional headers are required
## Conclusion
The MCP integration opens up unlimited possibilities for extending Julep agents with external capabilities. By following a standardized protocol, you can connect to any MCP-compatible server and instantly gain access to its tools, making your agents more powerful and adaptable.
For more information about the Model Context Protocol, visit the [official MCP documentation](https://modelcontextprotocol.io/). To explore available MCP servers, check out the [MCP server directory](https://github.com/modelcontextprotocol/servers).
# Cloudinary
Source: https://docs.julep.ai/integrations/mediafile/cloudinary
Learn how to use the Cloudinary integration with Julep
## Overview
Welcome to the Cloudinary integration guide for Julep! This integration allows you to manage and transform media files efficiently, enabling you to build workflows that require robust media processing capabilities. Whether you're uploading images or editing videos, this guide will walk you through the setup and usage.
## Prerequisites
To use the Cloudinary integration, you need an API key, API secret, and cloud name. You can obtain these by signing up at [Cloudinary](https://cloudinary.com/signup).
## How to Use the Integration
To get started with the Cloudinary integration, follow these steps to configure and create a task:
Add your API key, API secret, and cloud name to the tools section of your task. This will allow Julep to authenticate requests to Cloudinary on your behalf.
Use the following YAML configuration to define your media processing task:
```yaml Cloudinary Example [expandable] theme={"dark"}
name: Cloudinary Task
tools:
- name: cloudinary_tool
type: integration
integration:
provider: cloudinary
method: media_upload
setup:
cloudinary_cloud_name: "CLOUDINARY_CLOUD_NAME"
cloudinary_api_key: "CLOUDINARY_API_KEY"
cloudinary_api_secret: "CLOUDINARY_API_SECRET"
params: # Optional setup parameters
key1: "value1" # these are placeholders for the actual parameters
key2: "value2" # these are placeholders for the actual parameters
main:
- tool: cloudinary_tool
arguments:
file: https://example.com/image.jpg # this is a placeholder for the actual file
public_id: my_image # this is a placeholder for the actual public id
upload_params: # Optional upload parameters
param1: value1 # these are placeholders for the actual parameters
param2: value2 # these are placeholders for the actual parameters
return_base64: false
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Cloudinary Task".
* ***tools***: This section lists the tools or integrations being used. Here, `cloudinary_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `cloudinary` for Cloudinary.
* ***method***: Specifies the method to use, either `media_upload` or `media_edit`. Defaults to `media_edit` if not specified.
* ***setup***: Contains configuration details
* ***cloudinary\_cloud\_name***: (Required) The cloud name of the Cloudinary account.
* ***cloudinary\_api\_key***: (Required) The API key of the Cloudinary account.
* ***cloudinary\_api\_secret***: (Required) The API secret of the Cloudinary account.
* ***params***: (Optional) Optional parameters for the configuration.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`cloudinary_tool`).
* ***arguments***: Specifies the input parameters for the tool:
* ***file***: The URL of the file to upload. More details can be found in the [Cloudinary documentation](https://cloudinary.com/documentation/image_upload_api_reference#upload).
* ***public\_id***: (optional) Optional public ID for the uploaded file. Defaults to None.
* ***upload\_params***: (optional) Optional transformations for the upload. Defaults to None.
* ***return\_base64***: (optional) Whether to return the file in base64 encoding. Defaults to False.
* ***public\_id***: The public ID of the file to edit.
* ***transformation***: The transformations to apply to the file.
* ***return\_base64***: Whether to return the transformed file in base64 encoding.
Remember to replace `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` with your actual credentials.
The different parameters available for the Cloudinary integration can be found in the [Cloudinary API documentation](https://cloudinary.com/documentation/cloudinary_sdks#configuration_parameters).
## Conclusion
With the Cloudinary integration, you can efficiently manage and transform media files.
This integration provides a robust solution for media processing, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [Cloudinary documentation](https://cloudinary.com/documentation/python_quickstart).
# FFmpeg
Source: https://docs.julep.ai/integrations/mediafile/ffmpeg
Learn how to use the FFmpeg integration with Julep
## Overview
Welcome to the FFmpeg integration guide for Julep! This integration allows you to process media files using FFmpeg commands, enabling you to build workflows that require advanced media processing capabilities. Whether you're converting video formats or extracting audio, this guide will walk you through the setup and usage.
## How to Use the Integration
To get started with the FFmpeg integration, follow these steps to configure and create a task:
Use the following YAML configuration to define your FFmpeg command and process media files:
```yaml FFmpeg Example theme={"dark"}
name: FFmpeg Task
tools:
- name: ffmpeg_tool
type: integration
integration:
provider: ffmpeg
method: bash_cmd
main:
- tool: ffmpeg_tool
arguments:
cmd: $ "ffmpeg -i input.mp4 -vn -acodec copy output.aac"
file: base64_encoded_file # this is a placeholder for the actual file
```
The `base64_encoded_file` is the base64 encoded file to process which in this case is the `input.mp4` file.
The `file` argument can accept either a single base64 encoded string or a list of base64 encoded strings.
However, even when passing a list of files, the FFmpeg command can only use a single input file (single `-i` flag).
Multiple input files with multiple `-i` flags are not supported.
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "FFmpeg Task".
* ***tools***: This section lists the tools or integrations being used. Here, `ffmpeg_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `ffmpeg` for FFmpeg.
* ***method***: Indicates the method to be used, which is `bash_cmd` for FFmpeg. If not specified, the method will be `bash_cmd` by default.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`ffmpeg_tool`).
* ***arguments***: Specifies the input parameters for the tool:
* ***cmd***: The FFmpeg command to execute.
* ***file***: The base64 encoded file to process. Can be a single base64 encoded string or a list of base64 encoded strings.
Ensure your input file is base64 encoded and the FFmpeg command is correctly formatted for your specific use case.
## Conclusion
With the FFmpeg integration, you can efficiently process media files using powerful FFmpeg commands.
This integration provides a robust solution for media processing, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [FFmpeg documentation](https://ffmpeg.org/documentation.html).
# LlamaParse
Source: https://docs.julep.ai/integrations/mediafile/llamaparse
Learn how to use the LlamaParse integration with Julep
## Overview
Welcome to the LlamaParse integration guide for Julep! This integration allows you to parse documents efficiently, enabling you to build workflows that require document processing capabilities. Whether you're developing a document management system or need to extract information from files, this guide will walk you through the setup and usage.
## Prerequisites
To use the LlamaParse integration, you need an API key. You can obtain this key by signing up at [LlamaParse](https://docs.cloud.llamaindex.ai/llamaparse/getting_started/web_ui).
## How to Use the Integration
To get started with the LlamaParse integration, follow these steps to configure and create a task:
Add your API key to the tools section of your task. This will allow Julep to authenticate requests to LlamaParse on your behalf.
Use the following YAML configuration to define your document parsing task:
```yaml LlamaParse Example [expandable] theme={"dark"}
name: LlamaParse Task
tools:
- name: llama_parse
type: integration
integration:
provider: llama-parse
method: parse
setup:
llamaparse_api_key: "LLAMAPARSE_API_KEY"
params: # Optional setup parameters
key1: "value1" # these are placeholders for the actual parameters
key2: "value2" # these are placeholders for the actual parameters
main:
- tool: llama_parse
arguments:
file: base64_encoded_file # this is a placeholder for the actual file
filename: document.pdf # this is a placeholder for the actual filename
base64: true
params: # Optional arguments parameters
key1: value1 # these are placeholders for the actual parameters
key2: value2 # these are placeholders for the actual parameters
```
Deploy your task by creating a new execution.
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "LlamaParse Task".
* ***tools***: This section lists the tools or integrations being used. Here, `llama_parse` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `llama-parse` for LlamaParse.
* ***method***: Indicates the method to be used, which is `parse` for LlamaParse. If not specified, the method will be `parse` by default.
* ***setup***: Contains configuration Details
* ***llamaparse\_api\_key***: (Required) The API key of the LlamaParse account.
* ***params***: (Optional) Optional parameters for the configuration.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`llama_parse`).
* ***arguments***: Specifies the input parameters for the tool:
* ***file***: Can be either a base64 encoded file string or an array of http/https URLs to load
* ***filename***: (optional) The name of the file (only used with base64 encoded files). Defaults to None.
* ***base64***: (optional) Whether the input file is base64 encoded. Defaults to false.
* ***params***: (optional) Optional arguments parameters that can override setup parameters for specific tasks. Defaults to None.
The different parameters available for the LlamaParse integration can be found in the [LlamaParse API documentation](https://github.com/run-llama/llama_parse/blob/main/README.md).
Remember to replace `LLAMAPARSE_API_KEY` with your actual API key and ensure your file is base64 encoded if `base64` is set to true. Use the `params` field to pass any additional parameters required by your specific use case.
LlamaParse supports a wide range of different file types. For a full list of supported file types, please refer to the [LlamaParse documentation](https://github.com/run-llama/llama_parse/blob/main/llama_parse/utils.py).
## Conclusion
With the LlamaParse integration, you can efficiently process documents and extract valuable information.
This integration provides a robust solution for document processing, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [LlamaParse documentation](https://docs.cloud.llamaindex.ai/llamaparse/getting_started/python).
# Unstructured
Source: https://docs.julep.ai/integrations/mediafile/unstructured
Learn how to use the Unstructured.io integration with Julep
## Overview
Welcome to the Unstructured.io integration guide for Julep! This integration allows you to extract structured information from a wide variety of document formats, enabling you to build workflows that leverage advanced document processing capabilities. Whether you're developing a document analysis system, creating a RAG pipeline, or need to convert unstructured documents into structured data, this guide will walk you through the setup and usage.
## Prerequisites
To use the Unstructured.io integration, you need an API key. You can obtain this key by signing up at [Unstructured.io](https://unstructured.io/).
## How to Use the Integration
To get started with the Unstructured.io integration, follow these steps to configure and create a task:
Add your API key to the tools section of your task. This will allow Julep to authenticate requests to Unstructured.io on your behalf.
Use the following YAML configuration to define your document parsing task:
```yaml Unstructured Example theme={"dark"}
name: Unstructured Document Processing Task
tools:
- name: unstructured_processor
type: integration
integration:
provider: unstructured
method: parse
setup:
unstructured_api_key: "UNSTRUCTURED_API_KEY"
main:
- tool: unstructured_processor
arguments:
file: document_base64 # this is a placeholder for the actual file
filename: document.pdf # this is a placeholder for the actual filename
partition_params:
key1: value1 # these are placeholders for the actual parameters
key2: value2 # these are placeholders for the actual parameters
```
Deploy your task by creating a new execution.
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Unstructured Document Processing Task".
* ***tools***: This section lists the tools or integrations being used. Here, `unstructured_processor` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `unstructured` for Unstructured.io.
* ***method***: Indicates the method to be used, which is `parse` for Unstructured.io.
* ***setup***: Contains configuration details
* ***unstructured\_api\_key***: (Required) The API key for your Unstructured.io account.
* ***server\_url***: (Optional) Custom API endpoint URL if needed.
* ***server***: (Optional) Server name to use.
* ***url\_params***: (Optional) Dictionary of parameters to template the server URL with.
* ***timeout\_ms***: (Optional) Request timeout in milliseconds.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`unstructured_processor`).
* ***arguments***: Specifies the input parameters for the tool:
* ***file***: Base64 encoded file string.
* ***filename***: (Optional) The name of the file. Helpful for file type detection. In case no filename is provided, a random UUID will be generated.
* ***partition\_params***: (Optional) Advanced parameters for document processing. To see the full list of parameters, please refer to the [Unstructured.io API documentation](https://docs.unstructured.io/api-reference/partition/api-parameters).
The different parameters available for the Unstructured.io integration can be found in the [Unstructured.io API documentation](https://docs.unstructured.io/api-reference/).
* Remember to replace `UNSTRUCTURED_API_KEY` with your actual API key or use environment variables. For base64 encoded files, ensure your file is properly encoded before passing it to the integration.
* Unstructured.io supports a wide range of file types including PDFs, Word documents, PowerPoint presentations, Excel spreadsheets, emails, HTML, images, and more. For a full list of supported file types, please refer to the [Unstructured.io documentation](https://docs.unstructured.io/getting-started/supported-files).
## Conclusion
With the Unstructured.io integration, you can efficiently convert unstructured documents into structured data for analysis, search, and AI applications. This integration provides a powerful solution for document processing, enhancing your workflow's capabilities and enabling advanced RAG (Retrieval-Augmented Generation) pipelines.
For more information, please refer to the [Unstructured.io documentation](https://docs.unstructured.io/).
# Algolia
Source: https://docs.julep.ai/integrations/search/algolia
Learn how to use the Algolia search integration with Julep
## Overview
Welcome to the Algolia integration guide for Julep! This integration allows you to perform powerful searches across your Algolia indices, enabling workflows that require fast, relevant, and typo-tolerant search capabilities. Whether you're building a site search, content discovery system, or personalized recommendation engine, this guide will help you set up and use Algolia with Julep.
## Prerequisites
To use the Algolia integration, you need an Algolia account with an Application ID and API Key. You can sign up for an account at [Algolia](https://www.algolia.com/users/sign_up) and obtain your API credentials from the Algolia dashboard.
## How to Use the Integration
To get started with the Algolia integration, follow these steps to configure and create a task:
Add your Algolia Application ID and API Key to the tools section of your task. This will allow Julep to authenticate requests to Algolia on your behalf.
Use the following YAML configuration to define your search task:
```yaml Algolia Search Example theme={"dark"}
name: Algolia Search Task
tools:
- name: algolia_search
type: integration
integration:
provider: algolia
method: search
setup:
algolia_application_id: "ALGOLIA_APPLICATION_ID"
algolia_api_key: "ALGOLIA_API_KEY"
main:
- tool: algolia_search
arguments:
index_name: your_index_name # this is a placeholder for the actual index name
query: searchquery # this is a placeholder for the actual search query
hits_per_page: 10
attributes_to_retrieve: $ ["attribute1", "attribute2"] # this is a placeholder for the actual attributes to retrieve
```
## YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Algolia Search Task".
* ***tools***: This section lists the tools or integrations being used. Here, `algolia_search` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `algolia` for Algolia Search.
* ***method***: Indicates the method to be used, which is `search` for Algolia Search. This is the only supported method.
* ***setup***: Contains configuration details, such as:
* ***algolia\_application\_id***: Your Algolia Application ID.
* ***algolia\_api\_key***: Your Algolia API Key (should be a search-only API key for security).
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`algolia_search`).
* ***arguments***: Specifies the input parameters for the tool:
* ***index\_name***: The name of the Algolia index to search.
* ***query***: The search query to run against the index.
* ***hits\_per\_page*** (optional): Maximum number of results to return (default: 20).
* ***attributes\_to\_retrieve*** (optional): List of specific attributes to retrieve from the search results.
Remember to replace `ALGOLIA_APPLICATION_ID` and `ALGOLIA_API_KEY` with your actual Algolia credentials. For security, it's recommended to use search-only API keys with the minimal necessary permissions.
## Conclusion
The Algolia integration provides a powerful way to incorporate fast, relevant, and scalable search functionality into your Julep workflows. By leveraging Algolia's search capabilities, you can create sophisticated search experiences that help users find exactly what they're looking for.
For more information about Algolia's search parameters and capabilities, refer to the [Algolia Search API documentation](https://www.algolia.com/doc/api-reference/api-parameters/).
# ArXiv
Source: https://docs.julep.ai/integrations/search/arxiv
Learn how to use the ArXiv integration with Julep
## Overview
Welcome to the ArXiv integration guide for Julep! This integration allows you to access a vast repository of scientific papers and articles, enabling you to build workflows that require academic research data. Whether you're developing a research assistant or need scholarly articles for analysis, this guide will walk you through the setup and usage.
## How to Use the Integration
To get started with the ArXiv integration, follow these steps to configure and create a task:
Use the following YAML configuration to define your search parameters and request data from ArXiv:
```yaml ArXiv Search Example theme={"dark"}
name: ArXiv Search
tools:
- name: arxiv_search
type: integration
integration:
provider: arxiv
method: search
main:
- tool: arxiv_search
arguments:
query: machine learning
max_results: 10
download_pdf: False
sort_by: relevance
sort_order: descending
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "ArXiv Search".
* ***tools***: This section lists the tools or integrations being used. Here, `arxiv_search` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `arxiv` for ArXiv.
* ***method***: Indicates the method to be used, which is `search` for ArXiv. If not specified, the method will be `search` by default.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`arxiv_search`).
* ***arguments***: Specifies the input parameters for the tool:
* ***query***: The search query for the ArXiv search.
* ***max\_results***: (optional) The maximum number of results to return. Defaults to 5.
* ***download\_pdf***: (optional) Return base64 encoded pdfs. Defaults to False.
* ***sort\_by***: (optional) The sorting criterion for the results. Defaults to "relevance".
* ***sort\_order***: (optional) The sorting order for the results. Defaults to "descending".
Customize the `query` and other parameters to suit your specific search needs.
## Conclusion
With the ArXiv integration, you can easily access a wealth of academic papers and articles.
This integration provides a robust solution for accessing scholarly data, enhancing your workflow's research capabilities and user experience.
For more information, please refer to the [ArXiv API documentation](https://arxiv.org/help/api/index).
# Brave
Source: https://docs.julep.ai/integrations/search/brave
Learn how to use the Brave integration with Julep
## Overview
Welcome to the Brave Search integration guide for Julep! This integration allows you to perform web searches using Brave, enabling you to build workflows that require comprehensive search capabilities. Whether you're developing a search engine or need to retrieve web data, this guide will walk you through the setup and usage.
## Prerequisites
To use the Brave Search integration, you need an API key. You can obtain this key by signing up at [Brave](https://brave.com/search/api/).
## How to Use the Integration
To get started with the Brave Search integration, follow these steps to configure and create a task:
Add your API key to the tools section of your task. This will allow Julep to authenticate requests to Brave Search on your behalf.
Use the following YAML configuration to define your search task:
```yaml Brave Search Example theme={"dark"}
name: Brave Search Task
tools:
- name: brave_search
type: integration
integration:
provider: brave
method: search
setup:
brave_api_key: "BRAVE_API_KEY"
main:
- tool: brave_search
arguments:
query: latest technology trends
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Brave Search Task".
* ***tools***: This section lists the tools or integrations being used. Here, `brave_search` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `brave` for Brave Search.
* ***method***: Indicates the method to be used, which is `search` for Brave Search. If not specified, the method will be `search` by default.
* ***setup***: Contains configuration details, such as the API key (`api_key`) required for authentication.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`brave_search`).
* ***arguments***: Specifies the input parameters for the tool:
* ***query***: The search query for Brave Search.
Remember to replace `BRAVE_API_KEY` with your actual API key. Customize the `query` parameter to suit your specific search needs.
## Conclusion
With the Brave Search integration, you can efficiently perform web searches and retrieve relevant data.
This integration provides a robust solution for search capabilities, enhancing your workflow's functionality and user experience.
For more information, please refer to the [Brave Search API documentation](https://api.search.brave.com/app/documentation/web-search/get-started).
# Wikipedia
Source: https://docs.julep.ai/integrations/search/wikipedia
Learn how to use the Wikipedia integration with Julep
## Overview
Welcome to the Wikipedia integration guide for Julep! This integration allows you to access a vast repository of information from Wikipedia, enabling you to build workflows that require comprehensive data retrieval capabilities. Whether you're developing a knowledge base or need quick access to encyclopedic information, this guide will walk you through the setup and usage.
## Prerequisites
The Wikipedia integration does not require an API key. You can start using it immediately without any additional setup.
## How to Use the Integration
To get started with the Wikipedia integration, follow these steps to configure and create a task:
Use the following YAML configuration to define your search parameters and request data from Wikipedia:
```yaml Wikipedia Search Example theme={"dark"}
name: Wikipedia Search
tools:
- name: wikipedia_search
type: integration
integration:
provider: wikipedia
method: search
main:
- tool: wikipedia_search
arguments:
query: Artificial Intelligence
load_max_docs: 5
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Wikipedia Search".
* ***tools***: This section lists the tools or integrations being used. Here, `wikipedia_search` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `wikipedia` for Wikipedia.
* ***method***: Indicates the method to be used, which is `search` for Wikipedia. If not specified, the method will be `search` by default.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`wikipedia_search`).
* ***arguments***: Specifies the input parameters for the tool:
* ***query***: The search query string.
* ***load\_max\_docs***: (optional) Maximum number of documents to load. Defaults to 2. Range 1-10.
Customize the `query` and `load_max_docs` parameters to suit your specific search needs.
## Conclusion
With the Wikipedia integration, you can easily access a wealth of information from one of the largest online encyclopedias.
This integration provides a robust solution for data retrieval, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [Wikipedia API documentation](https://wikipedia.readthedocs.io/en/latest/).
# Supported Integrations
Source: https://docs.julep.ai/integrations/supported-integrations
List of supported integrations in Julep
## Overview
Julep supports a wide range of integrations to help you build powerful workflows. This page provides an overview of the supported integrations and their capabilities.
## Communication & Data
Tools for sending messages and accessing real-time data feeds.
| Integration | Description |
| -------------------------------------------------------------- | ------------------------------------------ |
| [Email](/integrations/communicationdata/email) | SMTP email sending and management |
| [Google Sheets](/integrations/communicationdata/google-sheets) | Spreadsheet data management and automation |
| [Weather](/integrations/communicationdata/weather) | Real-time weather data access |
## Media & File Processing
Solutions for handling documents, video, and digital assets.
| Integration | Description |
| ---------------------------------------------------- | -------------------------------------- |
| [LlamaParse](/integrations/mediafile/llamaparse) | Document parsing and extraction |
| [FFmpeg](/integrations/mediafile/ffmpeg) | Video processing and editing |
| [Cloudinary](/integrations/mediafile/cloudinary) | Cloud based Image and Video Processing |
| [Unstructured](/integrations/mediafile/unstructured) | Document parsing and extraction |
## Search
Access to search engines and knowledge bases for information retrieval.
| Integration | Description |
| ------------------------------------------- | ------------------------------------------- |
| [Arxiv](/integrations/search/arxiv) | Academic paper search |
| [Algolia](/integrations/search/algolia) | Site-specific search indexing and retrieval |
| [Brave](/integrations/search/brave) | Web search capabilities |
| [Wikipedia](/integrations/search/wikipedia) | Wikipedia article access |
## Web & Browser Automation
Tools for automated web interaction and data collection.
| Integration | Description |
| --------------------------------------------------------- | --------------------------- |
| [BrowserBase](/integrations/webbrowser/browserbase) | Headless browser automation |
| [Spider](/integrations/webbrowser/spider) | Web crawling and scraping |
| [Remote Browser](/integrations/webbrowser/remote-browser) | Remote browser control |
## Extensibility
Connect to external services and extend Julep's capabilities dynamically.
| Integration | Description |
| -------------------------------------- | ----------------------------------------------------------------------------------------- |
| [MCP](/integrations/extensibility/mcp) | Model Context Protocol - Connect to any MCP-compatible server for unlimited extensibility |
## Next Steps
* [Getting Started](/introduction/quickstart)
* [Tutorials](/tutorials)
* [Advanced Topics](/advanced)
# Supported Models
Source: https://docs.julep.ai/integrations/supported-models
Comprehensive guide to AI models and parameters supported by Julep
## Overview
Julep leverages LiteLLM to seamlessly connect you to a wide array of Language Models (LLMs). This integration offers incredible flexibility, allowing you to tap into models from various providers with a straightforward, unified interface.
With our unified API, switching between different providers is a breeze, ensuring you maintain consistent functionality across the board.
## Available Models
While we provide API keys for quick testing and development, you'll need to use your own API keys when deploying to production. This ensures you have full control over your usage and billing.
Looking for top-notch quality? Our curated selection of models delivers excellent outputs for all your use cases.
### Anthropic
Here are the Anthropic models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| -------------------------- | -------------- | ---------- | ------------ | ------ | ----- | ------- | ---------- |
| claude-3-haiku | 200K tokens | 4K tokens | ✅ | ✅ | ❌ | ❌ | Budget |
| claude-3-sonnet | 200K tokens | 4K tokens | ✅ | ✅ | ❌ | ❌ | Premium |
| claude-3.5-haiku | 200K tokens | 8K tokens | ✅ | ❌ | ❌ | ✅ | Standard |
| claude-3.5-sonnet | 200K tokens | 8K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| claude-3.5-sonnet-20240620 | 200K tokens | 4K tokens | ✅ | ✅ | ❌ | ❌ | Premium |
| claude-3.5-sonnet-20241022 | 200K tokens | 8K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| claude-3.7-sonnet | 200K tokens | 8K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| claude-haiku-4-5 | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| claude-opus-4 | 200K tokens | 32K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| claude-opus-4-1 | 200K tokens | 32K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| claude-sonnet-4 | 1M tokens | 64K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| claude-sonnet-4-5 | 200K tokens | 64K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
### Google
Here are the Google models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| ---------------------------- | -------------- | ---------- | ------------ | ------ | ----- | ------- | --------- |
| gemini-1.5-pro | 2M tokens | 8K tokens | ✅ | ✅ | ❌ | ❌ | Standard |
| gemini-1.5-pro-latest | 1M tokens | 8K tokens | ✅ | ✅ | ❌ | ❌ | Premium |
| gemini-2.0-flash | 1M tokens | 8K tokens | ✅ | ✅ | ✅ | ✅ | Budget |
| gemini-2.5-flash | 1M tokens | 65K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gemini-2.5-pro | 1M tokens | 65K tokens | ✅ | ✅ | ✅ | ✅ | Standard |
| gemini-2.5-pro-preview-03-25 | 1M tokens | 65K tokens | ✅ | ✅ | ❌ | ✅ | Standard |
| gemini-2.5-pro-preview-06-05 | 1M tokens | 65K tokens | ✅ | ✅ | ❌ | ✅ | Standard |
### OpenAI
Here are the OpenAI models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| --------------------- | -------------- | ----------- | ------------ | ------ | ----- | ------- | ---------- |
| gpt-4-turbo | 128K tokens | 4K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| gpt-4.1 | 1M tokens | 32K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| gpt-4.1-mini | 1M tokens | 32K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-4.1-nano | 1M tokens | 32K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-4o | 128K tokens | 16K tokens | ✅ | ✅ | ❌ | ✅ | Premium |
| gpt-4o-mini | 128K tokens | 16K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-5 | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Standard |
| gpt-5-2025-08-07 | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Standard |
| gpt-5-chat | 272K tokens | 128K tokens | ❌ | ✅ | ❌ | ✅ | Standard |
| gpt-5-chat-latest | 128K tokens | 16K tokens | ❌ | ✅ | ❌ | ✅ | Standard |
| gpt-5-mini | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-5-mini-2025-08-07 | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-5-nano | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| gpt-5-nano-2025-08-07 | 272K tokens | 128K tokens | ✅ | ✅ | ❌ | ✅ | Budget |
| o1 | 200K tokens | 100K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| o1-mini | 128K tokens | 65K tokens | ❌ | ✅ | ❌ | ✅ | Standard |
| o1-preview | 128K tokens | 32K tokens | ❌ | ✅ | ❌ | ✅ | Enterprise |
| o3-mini | 200K tokens | 100K tokens | ✅ | ❌ | ❌ | ✅ | Standard |
| o4-mini | 200K tokens | 100K tokens | ✅ | ✅ | ❌ | ✅ | Standard |
### Groq
Here are the Groq models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| --------------------------------------------- | -------------- | ----------- | ------------ | ------ | ----- | ------- | --------- |
| deepseek-r1-distill-llama-70b | 128K tokens | 128K tokens | ✅ | ❌ | ❌ | ❌ | Standard |
| gemma2-9b-it | 8K tokens | 8K tokens | ❌ | ❌ | ❌ | ❌ | Budget |
| llama-3.1-8b | 128K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| llama-3.1-8b-instant | 128K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| llama-3.3-70b-versatile | 128K tokens | 32K tokens | ✅ | ❌ | ❌ | ❌ | Standard |
| meta-llama/Llama-Guard-4-12B | 163K tokens | 163K tokens | ❌ | ❌ | ❌ | ❌ | Budget |
| meta-llama/llama-4-maverick-17b-128e-instruct | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| meta-llama/llama-4-scout-17b-16e-instruct | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| qwen/qwen3-32b | 131K tokens | 131K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
### OpenRouter
Here are the OpenRouter models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| ------------------------------------------- | -------------- | ----------- | ------------ | ------ | ----- | ------- | ---------- |
| deepseek-chat | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ✅ | Standard |
| deepseek/deepseek-r1-distill-llama-70b | 65K tokens | 8K tokens | ✅ | ❌ | ❌ | ✅ | Standard |
| deepseek/deepseek-r1-distill-qwen-32b | 65K tokens | 8K tokens | ✅ | ❌ | ❌ | ✅ | Standard |
| eva-llama-3.33-70b | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| eva-qwen-2.5-72b | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| hermes-3-llama-3.1-70b | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| l3.1-euryale-70b | 200K tokens | 100K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| l3.3-euryale-70b | 200K tokens | 100K tokens | ✅ | ✅ | ❌ | ✅ | Enterprise |
| magnum-v4-72b | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| meta-llama/llama-3.1-8b-instruct | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| meta-llama/llama-3.3-70b-instruct | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| meta-llama/llama-4-scout | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| mistral-large-2411 | 128K tokens | 128K tokens | ✅ | ❌ | ❌ | ❌ | Premium |
| openrouter/meta-llama/llama-4-maverick | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| openrouter/meta-llama/llama-4-maverick:free | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| openrouter/meta-llama/llama-4-scout | 131K tokens | 8K tokens | ✅ | ❌ | ❌ | ❌ | Budget |
| openrouter/meta-llama/llama-4-scout:free | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| perplexity/sonar | 128K tokens | Unknown | ❌ | ❌ | ❌ | ❌ | Standard |
| perplexity/sonar-deep-research | 128K tokens | Unknown | ❌ | ❌ | ❌ | ❌ | Premium |
| perplexity/sonar-pro | 200K tokens | 8K tokens | ❌ | ❌ | ❌ | ❌ | Premium |
| perplexity/sonar-reasoning | 128K tokens | Unknown | ❌ | ❌ | ❌ | ❌ | Standard |
| perplexity/sonar-reasoning-pro | 128K tokens | Unknown | ❌ | ❌ | ❌ | ❌ | Premium |
| qwen-2.5-72b-instruct | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
### Amazon Nova
Here are the Amazon Nova models supported by Julep:
| Model Name | Context Window | Max Output | Tool Calling | Vision | Audio | Caching | Cost Tier |
| -------------------- | -------------- | ---------- | ------------ | ------ | ----- | ------- | --------- |
| amazon/nova-lite-v1 | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| amazon/nova-micro-v1 | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
| amazon/nova-pro-v1 | Unknown | Unknown | ❌ | ❌ | ❌ | ❌ | Unknown |
### Embedding
Here are the embedding models supported by Julep:
| Model Name | Embedding Dimensions |
| ----------------------------- | -------------------- |
| Alibaba-NLP/gte-large-en-v1.5 | 1024 |
| BAAI/bge-m3 | 1024 |
| text-embedding-3-large | 1024 |
| vertex\_ai/text-embedding-004 | 1024 |
| voyage-3 | 1024 |
| voyage-multilingual-2 | 1024 |
Though the models mentioned above support different embedding dimensions, Julep uses fixed 1024 dimensions for all embedding models for now. We plan to support different dimensions in the future.
## Supported Parameters
Following are a list of different parameters that can be used to control the behavior of the models.
| Parameter | Range | Description |
| ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| temperature | 0.0 - 5.0 | Controls randomness in outputs. Higher values (e.g., 0.8) increase randomness, while lower values (e.g., 0.2) make output more focused and deterministic |
| top\_p | 0.0 - 1.0 | Alternative to temperature for nucleus sampling. Only tokens with cumulative probability \< top\_p are considered. We recommend adjusting either this or temperature, not both |
| max\_tokens | ≥ 1 | Maximum number of tokens to generate in the response |
| Parameter | Range | Description |
| ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
| frequency\_penalty | -2.0 - 2.0 | Penalizes tokens based on their frequency in the text. Positive values decrease repetition |
| presence\_penalty | -2.0 - 2.0 | Penalizes tokens based on their presence in the text. Positive values decrease likelihood of repeating content |
| repetition\_penalty | 0.0 - 2.0 | Penalizes repetition (1.0 is neutral). Values > 1.0 reduce likelihood of repeating content |
| length\_penalty | 0.0 - 2.0 | Penalizes based on generation length (1.0 is neutral). Values > 1.0 penalize longer generations |
| Parameter | Range | Description |
| ---------------- | ---------- | --------------------------------------------------------------------------------------------------- |
| min\_p | 0.0 - 1.0 | Minimum probability threshold compared to the highest token probability |
| seed | integer | For deterministic generation. Set a specific seed for reproducible results |
| stop | list\[str] | Up to 4 sequences where generation should stop |
| response\_format | object | Control output format: `{"type": "json_object"}` or `{"type": "json_schema", "json_schema": {...}}` |
Not all parameters are supported by every model. Please refer to the [LiteLLM documentation](https://docs.litellm.ai/completion/input) for more details.
**Response Format Support**: The `response_format` parameter is supported by OpenAI, Azure OpenAI, Google AI Studio (Gemini), Vertex AI, Bedrock, Anthropic, Groq, xAI (Grok-2+), Databricks, and Ollama. For the most up-to-date list, check the [LiteLLM JSON Mode documentation](https://docs.litellm.ai/docs/completion/json_mode).
**Best Practices:**
* Start with default values and adjust based on your needs
* Use temperature (0.0 - 1.0) for most cases
* Avoid setting multiple penalty parameters simultaneously
* Test different combinations for optimal results
Setting extreme values for multiple parameters may lead to unexpected behavior or poor quality outputs.
## Usage Guidelines
- **1.** Your budget and cost constraints
- **2.** How fast you need responses
- **3.** The quality you're aiming for
- **4.** The context window size you require
- **1.** Start with smaller models for development and testing
- **2.** Use larger context windows only when necessary
- **3.** Keep an eye on token usage to manage costs
For more information, please refer to the [LiteLLM documentation](https://docs.litellm.ai/providers).
# Browser Base
Source: https://docs.julep.ai/integrations/webbrowser/browserbase
Learn how to use the Browser Base integration with Julep
## Overview
Welcome to the Browser Base integration guide for Julep! This integration allows you to manage browser sessions and perform various actions, enabling you to build workflows that require browser automation capabilities. Whether you're testing web applications or automating web tasks, this guide will walk you through the setup and usage.
## Prerequisites
To use the Browserbase integration, you need an API key. You can obtain this key by signing up at [Browserbase](https://browserbase.com/signup).
To use the Browserbase integration, you need a Project ID. You can obtain this ID by signing up at [Browserbase](https://browserbase.com/signup).
## How to Use the Integration
To get started with the Browserbase integration, follow these steps to configure and create a task:
Add your API key and project ID to the tools section of your task. This will allow Julep to authenticate requests to Browserbase on your behalf.
Use the following YAML configuration to define your browser automation task:
```yaml Browser Base Example theme={"dark"}
name: Browser Base Task
tools:
- name: browserbase_tool
type: integration
integration:
provider: browserbase
method: create_session
setup:
api_key: "BROWSERBASE_API_KEY"
project_id: "BROWSERBASE_PROJECT_ID"
main:
- tool: browserbase_tool
arguments:
project_id: BROWSERBASE_PROJECT_ID
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Browser Base Task".
* ***tools***: This section lists the tools or integrations being used. Here, `browserbase_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `browserbase` for Browserbase.
* ***method***: Specifies the method to use, such as `create_session`, `list_sessions`, `get_session`, `complete_session`, `get_live_urls`, or `install_extension_from_github`. Defaults to `list_sessions` if not specified.
* ***setup***: Contains configuration details.
* ***api\_key***:(Required) The API key. Can be found in Settings.
* ***project\_id***: (Required) The Project ID. Can be found in Settings.
* ***api\_url***: (optional) The API URL. Defaults to [https://www.browserbase.com](https://www.browserbase.com)
* ***connect\_url***: (optional) The Connect URL. Defaults to wss\://connect.browserbase.com
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`browserbase_tool`).
* ***arguments***: Specifies the input parameters for the tool, which vary depending on the method used.
* ***project\_id***: The Project ID. Can be found in Settings.
* ***extension\_id***: (optional) The installed Extension ID. See Install Extension from GitHub.
* ***browser\_settings***: (optional) Browser settings object.
* ***timeout***: (optional) Duration in seconds after which the session will automatically end. Defaults to the Project's defaultTimeout.
* ***keep\_alive***: (optional) Set to true to keep the session alive even after disconnections. This is available on the Startup plan only.
* ***proxies***: (optional) Proxy configuration. Can be true for default proxy, or an array of proxy configurations.
* ***status***: The status of the sessions to list (Available options: RUNNING, ERROR, TIMED\_OUT, COMPLETED).
* ***id***: The session ID.
* ***id***: The session ID.
* ***id***: The session ID.
* ***repository\_name***: The GitHub repository name.
* ***ref***: Ref to install from a branch or tag.
* Remember to replace `BROWSERBASE_API_KEY` and `BROWSERBASE_PROJECT_ID` with your actual API key and project ID.
* Customize the `arguments` based on the method you choose to use.
## Conclusion
With the Browserbase integration, you can efficiently manage browser sessions and automate web tasks.
This integration provides a robust solution for browser automation, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [Browserbase API documentation](https://docs.browserbase.com/introduction).
# Remote Browser
Source: https://docs.julep.ai/integrations/webbrowser/remote-browser
Learn how to use the Remote Browser integration with Julep
## Overview
Welcome to the Remote Browser integration guide for Julep! This integration allows you to manage browser sessions and perform various actions, enabling you to build workflows that require browser automation capabilities.
Whether you're testing web applications or automating web tasks, this guide will walk you through the setup and usage.
## Prerequisites
To use the Remote Browser integration, you need to configure a remote browser to connect to. The integration then uses Playwright for browser automation to interact with the that remote browser.
## How to Use the Integration
To get started with the Remote Browser integration, follow these steps to configure and create a task:
Add your Remote Browser configuration (say Browserbase) to the tools section of your task. This will allow Julep to manage browser automation on your behalf to interact with the remote browser.
Use the following YAML configuration to perform browser actions in your task definition:
```yaml Remote Browser Example [expandable] theme={"dark"}
name: Browser Automation Task
tools:
- name: browserbase_tool
type: integration
integration:
provider: browserbase
method: create_session
setup:
project_id: "BROWSERBASE_PROJECT_ID"
- name: browser_tool
type: integration
integration:
provider: remote_browser
method: perform_action
setup:
width: 1920
height: 1080
main:
- tool: browserbase_tool
method: create_session
arguments:
project_id: BROWSERBASE_PROJECT_ID
- evaluate:
browser_session_id: $ _.id
connect_url: $ _.connect_url
- tool: browser_tool
arguments:
connect_url: $ _.connect_url
action: navigate
text: https://www.google.com
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Browser Automation Task".
* ***tools***: This section lists the tools or integrations being used. Here, `browser_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `remote_browser`.
* ***method***: Specifies the method to use, which is `perform_action`. Defaults to `perform_action` if not specified.
* ***setup***: Contains configuration details, which are the connection url and the browser size (width and height).
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`browser_tool`).
* ***method***: Specifies the method to use, which is `perform_action`.
* ***arguments***: Specifies the input parameters for the tool:
* ***action***: The type of action to perform.
- `key`: Send keyboard input
- `type`: Type text into an input field
- `mouse_move`: Move the mouse cursor to coordinates
- `left_click`: Perform a left mouse click
- `left_click_drag`: Click and drag with left mouse button
- `right_click`: Perform a right mouse click
- `middle_click`: Perform a middle mouse click
- `double_click`: Perform a double click
- `screenshot`: Take a screenshot
- `cursor_position`: Get current cursor position
- `navigate`: Navigate to a URL
- `refresh`: Refresh the current page
* ***text***: The text to type in the input field.
* ***coordinate***: The coordinates to click on the screen to move the mouse.
* Remember to replace `BROWSERBASE_PROJECT_ID` with your actual project ID.
* Make sure to properly configure browser settings and action parameters for your use case.
## Conclusion
With the Remote Browser integration, you can efficiently automate browser interactions in your workflows. This integration provides a robust solution for web automation, enhancing your workflow's capabilities and reliability.
For more information, please refer to the [Playwright documentation](https://playwright.dev/).
# Spider Crawler
Source: https://docs.julep.ai/integrations/webbrowser/spider
Learn how to use the Spider Crawler integration with Julep
## Overview
Welcome to the Spider Crawler integration guide for Julep! This integration allows you to crawl websites and extract data, enabling you to build workflows that require web scraping capabilities. Whether you're gathering data for analysis or monitoring web content, this guide will walk you through the setup and usage.
## Prerequisites
To use the Spider integration, you need an API key. You can obtain this key by signing up at [Spider](https://spider.cloud/).
## How to Use the Integration
To get started with the Spider integration, follow these steps to configure and create a task:
Add your API key to the tools section of your task. This will allow Julep to authenticate requests to Spider on your behalf.
Use the following YAML configuration to define your web crawling task:
```yaml Spider Example theme={"dark"}
name: Spider Task
tools:
- name: spider_tool
type: integration
integration:
provider: spider
method: crawl
setup:
spider_api_key: "SPIDER_API_KEY"
main:
- tool: spider_tool
method: crawl
arguments:
url: $ _.url
params: # Optional parameters
key1: value1 # this a placeholder for the actual parameters
content_type: application/json
```
### YAML Explanation
* ***name***: A descriptive name for the task, in this case, "Spider Task".
* ***tools***: This section lists the tools or integrations being used. Here, `spider_tool` is defined as an integration tool.
* ***type***: Specifies the type of tool, which is `integration` in this context.
* ***integration***: Details the provider and setup for the integration.
* ***provider***: Indicates the service provider, which is `spider` for Spider.
* ***method***: Specifies the method to use, such as `crawl`, `links`, `screenshot`, or `search`. Defaults to `crawl` if not specified.
* ***setup***: Contains configuration details, such as the API key (`spider_api_key`) required for authentication.
* ***main***: Defines the main execution steps.
* ***tool***: Refers to the tool defined earlier (`spider_tool`).
* ***arguments***: Specifies the input parameters for the tool:
* ***url***: The URL for which to fetch data.
* ***params***: (optional) The parameters for the Spider API. Defaults to None.
* ***content\_type***: (optional) The content type to return. Default is "application/json". Other options: "text/csv", "application/xml", "application/jsonl".
Remember to replace `SPIDER_API_KEY` with your actual API key. Customize the `url`, `params`, and `content_type` parameters to suit your specific needs.
The different parameters available depending on the method used for the Spider integration can be found in the [Spider API documentation](https://spider.cloud/api).
## Conclusion
With the Spider integration, you can efficiently crawl websites and extract valuable data.
This integration provides a robust solution for web scraping, enhancing your workflow's capabilities and user experience.
For more information, please refer to the [Spider API documentation](https://spider.cloud/api).
# Developer Orientation
Source: https://docs.julep.ai/introduction/developer-orientation
Overview of the repository structure and key resources for Julep developers
## Overview
This page provides a high-level tour of the Julep repository so you know where to find key components and documentation.
## Main Directories
### `agents-api/`
Core FastAPI service that defines agents, tasks, sessions, and runs workflows.
### `memory-store/`
PostgreSQL-based service used for vector storage and other persistent data.
### `integrations-service/`
Houses adapters for external services and tools that agents can call.
### `sdks/`
Submodules containing the Python and Node.js SDK implementations.
## Developer Resources
* **CLI Usage** – See the [CLI guide](/julepcli/introduction) and [Command Reference](/julepcli/commands).
* **TypeSpec Definitions** – API schemas live in the [`typespec/`](https://github.com/julep-ai/julep/tree/dev/typespec) directory. The generated OpenAPI spec is available at [`openapi.yaml`](https://github.com/julep-ai/julep/blob/dev/openapi.yaml).
* **SDK References** – Python and Node.js SDK docs are in the [SDK section](/sdks/index) of the documentation.
## Learn More
Refer to the [Quick Start](/introduction/quickstart) to try Julep right away and the [Installation Guide](/introduction/install) for setup instructions.
## Next Steps for Learning
* 📚 Explore more examples in our [Cookbook](https://github.com/julep-ai/julep/tree/dev/cookbooks)
* 🔧 Learn about [Tool Integration](https://docs.julep.ai/docs/tools/overview)
* 🧠 Understand [Agent Memory](https://docs.julep.ai/docs/agents/memory)
* 🔄 Dive into [Complex Workflows](https://docs.julep.ai/docs/tasks/workflows)
> \[!TIP]
> 💡 Checkout more tutorials in the [Tutorials](https://docs.julep.ai/docs/tutorials/) section of the documentation.
>
> 💡 If you are a beginner, we recommend starting with the [Quickstart Guide](https://docs.julep.ai/docs/introduction/quickstart).
>
> 💡 If you are looking for more ideas, check out the [Ideas](https://github.com/julep-ai/julep/blob/dev/cookbooks/IDEAS.md) section of the repository.
>
> 💡 If you are more into cookbook style recipes, check out the [Cookbook](https://github.com/julep-ai/julep/tree/dev/cookbooks) section of the repository.
# Installation
Source: https://docs.julep.ai/introduction/install
Step-by-step installation instructions for different environments
## Overview
This guide covers the installation of Julep in various environments and configurations.
### Prerequisites
Before installing Julep, ensure you have:
* Python 3.8+ or Node.js 16+ installed
* pip (for Python) or npm/bun (for Node.js) package manager
* A Julep API key ([Get one here](https://dashboard.julep.ai))
### Package Installation
1. Using pip:
```bash theme={"dark"}
pip install julep
```
2. Using poetry:
```bash theme={"dark"}
poetry add julep
```
3. Using pipenv:
```bash theme={"dark"}
pipenv install julep
```
1. Using npm:
```bash theme={"dark"}
npm install @julep/sdk
```
2. Using yarn:
```bash theme={"dark"}
yarn add @julep/sdk
```
3. Using bun:
```bash theme={"dark"}
bun add @julep/sdk
```
### Environment Setup
#### Setting up Environment Variables
It's recommended to use environment variables for sensitive information like API keys:
1. Create a `.env` file in your project root:
```bash theme={"dark"}
JULEP_API_KEY=your_api_key_here
JULEP_ENVIRONMENT=production # or dev (development)
```
2. Load the environment variables in your code:
```python Python theme={"dark"}
import os
from dotenv import load_dotenv
from julep import Julep
load_dotenv()
client = Julep(
api_key=os.getenv('JULEP_API_KEY'),
environment=os.getenv('JULEP_ENVIRONMENT', 'production')
)
```
```javascript Node.js theme={"dark"}
import dotenv from 'dotenv';
import { Julep } from '@julep/sdk';
dotenv.config();
const client = new Julep({
apiKey: process.env.JULEP_API_KEY,
environment: process.env.JULEP_ENVIRONMENT || 'production'
});
```
### Verification
To verify your installation:
```python Python theme={"dark"}
from julep import Julep
import os
from dotenv import load_dotenv
load_dotenv()
client = Julep(
api_key=os.getenv('JULEP_API_KEY'),
environment=os.getenv('JULEP_ENVIRONMENT', 'production')
)
# Test connection
agent = client.agents.create(
name="Test Agent",
model="claude-3.5-haiku",
about="A test agent"
)
print(f"Successfully created agent: {agent.id}")
```
```javascript Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
import dotenv from 'dotenv';
dotenv.config();
const client = new Julep({
apiKey: process.env.JULEP_API_KEY,
environment: process.env.JULEP_ENVIRONMENT || 'production'
});
// Test connection
const agent = await client.agents.create({
name: "Test Agent",
model: "claude-3.5-haiku",
about: "A test agent"
});
console.log(`Successfully created agent: ${agent.id}`);
```
### IDE Integration
Enhance your development experience by accessing Julep documentation directly in your IDE through the [Context7 MCP server](https://context7.com/julep-ai/julep). This integration allows you to:
* Access Julep documentation without leaving your IDE
* Get instant answers about Julep APIs and concepts
* View code examples and best practices inline
To set up the integration, visit [Context7 Julep Documentation](https://context7.com/julep-ai/julep) and follow the instructions for your specific IDE.
### Troubleshooting
Common installation issues and solutions:
1. **API Key Issues**
* Ensure your API key is valid and properly set in environment variables
* Check if you're using the correct environment (production/development)
2. **Version Compatibility**
* Make sure you're using compatible versions of Python/Node.js
* Update to the latest SDK version if you encounter issues
3. **Docker Issues**
* Verify Docker is running and has sufficient resources
* Check if required ports are available and not blocked
### Next Steps
Now that you have Julep installed, you can:
Create your first Julep agent and task
# Welcome to Julep
Source: https://docs.julep.ai/introduction/julep
Julep is a platform for creating AI agents that remember past interactions and can perform complex tasks. It offers long-term memory and manages multi-step processes.
Julep enables the creation of multi-step tasks incorporating decision-making, loops, parallel processing, and integration with numerous external tools and APIs.
While many AI applications are limited to simple, linear chains of prompts and API calls with minimal branching, Julep is built to handle more complex scenarios which:
* Have multiple steps,
* Make decisions based on model outputs,
* Spawn parallel branches,
* Use lots of tools, and
* Run for a long time.
Julep offers a comprehensive set of features designed to help you build sophisticated AI workflows.
Imagine you want to build an AI agent that can do more than just answer simple questions — it needs to handle complex tasks, remember past interactions, and maybe even use other tools or APIs.
Now since you understand the problem Julep is solving, let's explore the key features that make Julep stand out.
## Core Features
Create agents that maintain context and remember information across multiple interactions. Agents can learn from past conversations and apply that knowledge to future tasks.
Keep track of conversation history and context across multiple interactions. Sessions can be paused, resumed, and maintain their state indefinitely.
Build complex workflows with decision-making capabilities, loops, and conditional logic. Tasks can be as simple or as sophisticated as needed.
Handle long-running tasks that can run indefinitely. Tasks are automatically managed, with built-in support for retries and error handling.
## Advanced Capabilities
Access a wide range of built-in tools and easily integrate with external APIs. Tools can be added to agents to extend their capabilities.
Automatic retry mechanisms for failed steps, message resending, and robust task management keep your workflows running smoothly.
Built-in support for Retrieval-Augmented Generation. Use Julep's document store to build systems that can retrieve and utilize your own data.
Seamlessly integrate with your existing infrastructure using our comprehensive SDKs for Python and Node.js.
## Exploring the Documentation
Depending on your use case, here are different ways to explore our documentation:
Start with the Quick Start guide, then dive into Core Concepts and Task Management to build AI-powered applications.
Focus on Architecture Deep Dive, Security Features, and Integration Guides to understand enterprise-grade deployment.
Explore our Model Configuration, RAG Implementation, and Advanced Features sections to experiment with AI capabilities.
Check out our API Reference, SDK Documentation, and Integration Tutorials to connect Julep with existing systems.
## Next Steps
Detailed setup instructions
Essential concepts and terminology
Learn from practical tutorials
## Need Help?
Get help and share ideas with other Julep developers
Reach out to our team for assistance
# Quick Start
Source: https://docs.julep.ai/introduction/quickstart
Quick example to get started with Julep
## Overview
This guide will help you get started with Julep in just 5 minutes. You'll learn how to create your first AI agent and execute a simple task.
### What we'll build
We'll build a simple agent that uses tasks to write a short story.
```yaml Agent YAML theme={"dark"}
name: Story Generator
model: claude-3.5-sonnet
about: A helpful AI assistant that specializes in writing and editing.
```
```yaml Task YAML theme={"dark"}
name: Write a short story
description: Write a short story about a magical garden
main:
- prompt:
- role: system
content: You are a creative story writer.
- role: user
content: $ f'Write a short story about {steps[0].input.topic}'
```
### Prerequisites
* Python 3.8+ or Node.js 16+
* A Julep API key ([Get one here](https://dashboard.julep.ai))
### Step 1: Install Julep
Choose your preferred language:
```bash Python theme={"dark"}
pip install julep
```
```bash Node.js theme={"dark"}
npm install @julep/sdk
# or
bun add @julep/sdk
```
### Step 2: Initialize the Client
```python Python theme={"dark"}
from julep import Julep
client = Julep(api_key="your_julep_api_key")
```
```javascript Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
const client = new Julep({
apiKey: 'your_julep_api_key'
});
```
### Step 3: Create Your First Agent
Let's create a simple AI agent that can help with writing tasks:
```python Python theme={"dark"}
agent = client.agents.create(
name="Writing Assistant",
model="claude-3.5-sonnet",
about="A helpful AI assistant that specializes in writing and editing."
)
```
```javascript Node.js theme={"dark"}
const agent = await client.agents.create({
name: "Writing Assistant",
model: "claude-3.5-sonnet",
about: "A helpful AI assistant that specializes in writing and editing."
});
```
### Step 4: Create a Simple Task
Let's create a task that generates a short story based on a given topic:
```python Python theme={"dark"}
import yaml
task_definition = yaml.safe_load("""
name: Story Generator
description: Generate a short story based on a given topic
main:
- prompt:
- role: system
content: You are a creative story writer.
- role: user
content: $ f'Write a short story about {steps[0].input.topic}'
""")
task = client.tasks.create(
agent_id=agent.id,
**task_definition # Unpack the task definition
)
```
```javascript Node.js theme={"dark"}
const yaml = require("yaml");
const task_definition = `
name: Story Generator
description: Generate a short story based on a given topic
main:
- prompt:
- role: system
content: You are a creative story writer.
- role: user
content: $ f'Write a short story about {steps[0].input.topic}'
`;
const task = await client.tasks.create(
agent.id,
yaml.parse(task_definition)
);
```
### Step 5: Execute the Task
Now let's run the task with a specific topic:
```python Python theme={"dark"}
execution = client.executions.create(
task_id=task.id,
input={"topic": "a magical garden"}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```javascript Node.js [expandable] theme={"dark"}
const execution = await client.executions.create(
task.id,
{
input: { topic: "a magical garden" }
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
### Next Steps
Congratulations! You've created your first Julep agent and executed a task. Here's what you can explore next:
Learn about the core concepts of Julep
Explore tutorials to learn how to use Julep
# Command Reference
Source: https://docs.julep.ai/julepcli/commands
Commands for managing Julep projects and resources
## Overview
It is still in experimental phase and is not yet complete. In case of any issues, please reach out to us on [Discord](https://discord.com/invite/JTSBGRZrzj) or [email](mailto:hey@julep.ai).
The `julep` CLI is a comprehensive command-line interface for interacting with the Julep platform.
Following are the available commands.
## Authentication
```bash theme={"dark"}
julep auth
```
The `julep auth` command is used to authenticate your Julep CLI. This will prompt you to enter your API key and save it to the configuration file.
**Example:**
```bash theme={"dark"}
# Basic authentication with interactive prompt
julep auth
# Output:
# Enter your Julep API key: **********************
# Authentication successful!
```
```bash theme={"dark"}
julep auth --api-key your_julep_api_key
```
The `julep auth --api-key your_julep_api_key` command is used to authenticate your Julep CLI with a specific API key.
Your Julep API key
**Examples:**
```bash theme={"dark"}
# Authenticate with API key directly
julep auth --api-key "julep_1234567890abcdef"
# Using environment variable
export JULEP_API_KEY="julep_1234567890abcdef"
julep auth --api-key $JULEP_API_KEY
```
```bash theme={"dark"}
julep auth --api-key your_key --environment staging
```
The `julep auth --api-key your_key --environment staging` command is used to authenticate your Julep CLI with a specific API key and environment.
Your Julep API key
Environment to use (production/staging)
**Examples:**
```bash theme={"dark"}
# Authenticate with staging environment
julep auth --api-key "julep_1234567890abcdef" --environment staging
# Authenticate with production environment
julep auth --api-key "julep_1234567890abcdef" --environment production
# Using environment variables
export JULEP_API_KEY="julep_1234567890abcdef"
export JULEP_ENV="staging"
julep auth --api-key $JULEP_API_KEY --environment $JULEP_ENV
```
You can get your API key from the [Julep Dashboard](https://dashboard.julep.ai/).
## Project Management
```bash theme={"dark"}
julep init --template "Template Name" --path "Destination Path" --yes
```
The `julep init` command is used to initialize a new Julep project using a predefined template. You can check the list of available templates in our [library](https://github.com/julep-ai/library).
Name of the template to use from the library repository (default: "hello-world")
Destination directory for where you want to initialize the project (default: current directory)
Skip confirmation prompt
**Examples:**
```bash theme={"dark"}
# Initialize a project with the default template (hello-world template)
julep init
# Initialize a project with a specific template
julep init --template "profiling-recommending" --path "./my-project" # Will be initialized in ./my-project/profiling-recommending
# Initialize a project and skip confirmation
julep init --yes
```
```bash theme={"dark"}
julep sync [--force-local] [--force-remote] [--source "Source Path"]
```
The `julep sync` command is used to synchronize the local project with the Julep platform.
Force local files to take precedence
Force remote state to take precedence
Watch for changes and synchronize automatically.
> **Note:** when watch mode is enabled, only local-to-remote sync `force-local` is supported. Use `--force-remote` only with watch mode turned off.
Source directory to sync from (default: current directory)
**Examples:**
```bash theme={"dark"}
# Basic sync
julep sync
# Force local changes to override remote
julep sync --force-local
# Force remote state to override local
julep sync --force-remote
# Sync with specific source directory
julep sync --source ./my-project
# Watch for changes and synchronize automatically
julep sync --watch
```
```bash theme={"dark"}
julep import --agent --id agent_id --output path
```
The `julep import` command is used to import an `agent`, `task`, or `tool` from the Julep platform.
> **Note:** Importing tasks and tools is not yet supported; only agents are supported.
Specify that you want to import an agent
ID of the agent to import
Path to the source directory. Defaults to current working directory
Path to save the imported agent (default: `/src/agents/.yaml`)
Skip confirmation prompt
**Examples:**
```bash theme={"dark"}
# Import agent to default directory
julep import --agent --id "00000000-0000-0000-0000-000000000000"
# Import agent with specific filename
julep import --agent --id "00000000-0000-0000-0000-000000000000" --output ./agents/my-agent.yaml
# Import agent and skip confirmation
julep import --agent --id "00000000-0000-0000-0000-000000000000" --yes
```
## Task Execution
```bash theme={"dark"}
julep run --task --input '{"key": "value"}'
```
The `julep run` command is used to execute a task.
ID of the task to execute
Input data for the task
Path to a json file containing the input to execute the task with
Wait for the task to complete before exiting, stream logs to stdout
**Examples:**
```bash theme={"dark"}
# Run task with inline JSON input
julep run --task "00000000-0000-0000-0000-000000000000" --input '{"prompt": "Write a story about a magical forest"}'
# Run task with input from file
julep run --task "00000000-0000-0000-0000-000000000000" --input-file ./inputs/story-params.json
# Run task and wait for completion
julep run --task "00000000-0000-0000-0000-000000000000" --input '{"style": "watercolor"}' --wait
# Run task that doesn't require input (empty object)
julep run --task "00000000-0000-0000-0000-000000000000" --input '{}'
# Invalid task ID
julep run --task bad-id --input '{}'
# -> Error creating execution: Task not found
```
```bash theme={"dark"}
julep logs --execution-id exec_id [--tail]
```
The `julep logs` command is used to view execution logs.
ID of the execution to view logs for
Continuously stream logs as they are generated
**Examples:**
```bash theme={"dark"}
# View logs for a specific execution
julep logs --execution-id "00000000-0000-0000-0000-000000000000"
# Stream logs in real-time
julep logs --execution-id "00000000-0000-0000-0000-000000000000" --tail
# Get logs in JSON format
julep logs --execution-id "00000000-0000-0000-0000-000000000000" --json
```
## Agent Management
The commands in `julep agents` are used to manage your agents.
```bash theme={"dark"}
julep agents create --name "Agent Name" \
--model "Model Name" \
--about "Agent Description" \
--metadata '{"key": "value"}' \
--instructions "Instruction"
```
The `julep agents create` command is used to create a new agent.
Name of the agent
Model to be used (e.g., gpt-4). You can find the list of models [here](/integrations/supported-models#available-models)
Description of the agent
Additional metadata (JSON format)
Instructions for the agent (can be repeated)
Path to agent definition file
**Examples:**
```bash theme={"dark"}
# Create a basic agent
julep agents create --name "Story Writer" --model "gpt-4"
# Create agent with full configuration
julep agents create \
--name "Content Assistant" \
--model "gpt-4" \
--about "AI assistant for content creation" \
--metadata '{"type": "content", "version": "1.0"}' \
--instructions "Focus on creative writing" \
--instructions "Maintain professional tone"
# Create agent from definition file
julep agents create --definition ./agents/content-assistant.yaml
```
> **Note:** When creating an agent, the agent doesn't get automatically imported to the project. You can run `julep import --agent --id ` to import the agent to the project.
```bash theme={"dark"}
julep agents update --id agent_id \
--name "New Name" \
--model "New Model"
```
The `julep agents update` command is used to update an existing agent.
ID of the agent to update
New name for the agent
New model for the agent
New description for the agent
Additional metadata (JSON format)
New instructions for the agent (can be repeated)
**Examples:**
```bash [expandable] theme={"dark"}
# Update agent name
julep agents update --id "agent_abc123" --name "Enhanced Writer"
# Update multiple properties
julep agents update \
--id "agent_abc123" \
--name "Content Pro" \
--model "gpt-4" \
--about "Professional content creation assistant"
# Update metadata
julep agents update \
--id "agent_abc123" \
--metadata '{"type": "content", "version": "2.0"}'
# Update instructions
julep agents update \
--id "agent_abc123" \
--instructions "New instruction 1" \
--instructions "New instruction 2"
# Update from definition file
julep agents update --id "agent_abc123" --definition ./agents/updated-agent.yaml
```
```bash theme={"dark"}
julep agents delete --id agent_id [--force]
```
The `julep agents delete` command is used to delete an existing agent.
ID of the agent to delete
Skip confirmation prompt
**Examples:**
```bash theme={"dark"}
# Delete with confirmation prompt
julep agents delete --id "00000000-0000-0000-0000-000000000000"
# Force delete without confirmation
julep agents delete --id "00000000-0000-0000-0000-000000000000" --force
# Delete multiple agents using a bash for loop
for id in agent_id1 agent_id2 agent_id3; do
julep agents delete --id "$id" --force
done
```
```bash theme={"dark"}
julep agents list [--metadata-filter '{"key": "value"}'] [--json]
```
The `julep agents list` command is used to list all agents.
Filter agents by metadata
Output in JSON format
**Examples:**
```bash theme={"dark"}
# List all agents
julep agents list
# List agents with JSON output
julep agents list --json
# Filter agents by metadata
julep agents list --metadata-filter '{"type": "content"}'
# List agents and process with jq
julep agents list --json | jq '.[] | select(.model=="gpt-4")'
# Save agent list to file
julep agents list --json > agents.json
```
```bash theme={"dark"}
julep agents get --id agent_id [--json]
```
The `julep agents get` command is used to get details of a specific agent.
ID of the agent to retrieve
Output in JSON format
**Examples:**
```bash theme={"dark"}
# Get agent details
julep agents get --id "agent_abc123"
# Get agent details in JSON format
julep agents get --id "agent_abc123" --json
# Save agent details to file
julep agents get --id "agent_abc123" --json > agent.json
# Get and process with jq
julep agents get --id "agent_abc123" --json | jq '.instructions'
```
## Task Management
```bash theme={"dark"}
julep tasks create --name "Task Name" \
--agent-id agent_id \
--definition path/to/task.yaml \
--metadata '{"status": "beta"}' \
--inherit-tools
```
The `julep tasks create` command is used to create a new task.
ID of the associated agent
Name of the task
Path to task definition file
Additional metadata (JSON format)
Inherit tools from agent
Import to project after creating
```bash theme={"dark"}
julep tasks update --id task_id \
--name "New Name" \
--agent-id new_agent_id \
--definition new/path/to/task.yaml
```
The `julep tasks update` command is used to update an existing task.
ID of the task to update
New name for the task
New agent ID for the task
Path to new task definition file
New description for the task
New metadata for the task
Whether to inherit tools from the agent
```bash theme={"dark"}
julep tasks delete --id task_id [--force]
```
The `julep tasks delete` command is used to delete an existing task.
ID of the task to delete
Skip confirmation prompt
```bash theme={"dark"}
julep tasks list [--agent-id agent_id] [--json]
```
The `julep tasks list` command is used to list all tasks.
Filter by associated agent ID
Output in JSON format
```bash theme={"dark"}
julep tasks get --id task_id [--json]
```
The `julep tasks get` command is used to get details of a specific task.
ID of the task to retrieve
Output in JSON format
## Tool Management
```bash theme={"dark"}
julep tools create --name "Tool Name" \
--type tool_type \
--agent-id agent_id \
--definition path/to/config.yaml
```
The `julep tools create` command is used to create a new tool.
ID of the associated agent
Name of the tool
Type of the tool
Path to tool definition file
Description of the tool
Additional metadata (JSON format)
Import to project after creating
```bash theme={"dark"}
julep tools update --id tool_id \
--name "New Name" \
--agent-id new_agent_id \
--definition new/path/to/config.yaml
```
The `julep tools update` command is used to update an existing tool.
ID of the tool to update
New name for the tool
New agent ID for the tool
Path to new tool definition file
New description for the tool
New metadata for the tool
```bash theme={"dark"}
julep tools delete --agent-id agent_id --id tool_id [--force]
```
The `julep tools delete` command is used to delete an existing tool.
ID of the agent the tool is associated with
ID of the tool to delete
Skip confirmation prompt
```bash theme={"dark"}
julep tools list --agent-id agent_id [--json]
```
The `julep tools list` command is used to list all tools for a given agent.
Filter by associated agent ID
Output in JSON format
A future release will add a `julep tools get` command for retrieving a tool by ID.
## Execution Management
```bash theme={"dark"}
julep executions create --task task_id --input '{"key": "value"}'
```
The `julep executions create` command is used to create a new execution for a task.
ID or name of the task to execute
JSON string representing the input for the task (defaults to )
Path to a file containing the input for the task
```bash theme={"dark"}
julep executions list [--task-id task_id] [--json]
```
The `julep executions list` command is used to list all executions or filter based on criteria.
Filter executions by associated task ID
Output the list in JSON format
```bash theme={"dark"}
julep executions cancel --id execution_id
```
The `julep executions cancel` command is used to cancel an existing execution.
ID of the execution to cancel
The CLI will soon include a `julep assistant` command that launches an interactive prompt for generating commands from plain language. This feature is still under development.
## Miscellaneous
```bash theme={"dark"}
julep --version
```
The `julep --version` or `julep -v` command displays the current version of the Julep CLI.
```bash theme={"dark"}
julep --help
```
The `julep --help` or `julep -h` command displays help information for the Julep CLI.
## Best Practices
- Keep agent, task, and tool definitions in separate directories under `src/`
- Use meaningful file names that reflect their purpose
- Follow the standard project structure
- **DO** commit `julep.yaml` to version control
- **DO** commit `julep-lock.json` to version control
- Document project dependencies and requirements
- Use `--json` flag for machine-readable output
- Use `--quiet` for scripting and automation
- Always provide required parameters
### File Management
* **Project Configuration**: Keep your `julep.yaml` clean and well-organized
* **Lock File**: Use `julep-lock.json` to track remote state and relationships
* **Source Files**: Organize definitions in appropriate directories under `src/`
### Workflow Tips
* Use `julep sync` regularly to keep local and remote states in sync
* Review changes with `--json` output before applying updates
* Use the assistant mode for complex workflows: `julep assistant`
### Security Best Practices
* Store API keys securely
* Use environment-specific configurations
* Review permissions before executing destructive commands
* Use `--force` flags cautiously
Always review the changes before using force flags (`--force-local`, `--force-remote`) as they can override remote or local state.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# Getting Started
Source: https://docs.julep.ai/julepcli/introduction
Getting started with Julep CLI
## Overview
The `julep` CLI provides a comprehensive command-line interface for interacting with the Julep platform. It enables you to manage agents, tasks, tools, and projects directly from your terminal.
## Installation
```bash pip theme={"dark"}
pip install julep-cli
```
Note: While the package name is `julep-cli`, the installed command is simply `julep`.
## Configuration
The CLI stores configuration data in `~/.config/julep/config.yml`. This file is created automatically after authenticating using `julep auth` (see [Authentication](/julepcli/commands#authentication) section).
```yaml config.yml theme={"dark"}
api_key: "your_api_key_here"
environment: "production"
```
## Project Structure
A Julep project follows a standard directory structure:
```plaintext theme={"dark"}
project-name/
├── README.md # Documentation and usage instructions
├── julep.yaml # Project configuration and entrypoint
├── julep-lock.json # Lock file tracking server state
└── src/ # Source directory
├── agents/ # Agent definitions
│ └── agent.yaml
├── tasks/ # Task definitions
│ └── task.yaml
└── tools/ # Tool definitions
└── tool.yaml
```
The `julep.yaml` file defines your project configuration:
```yaml julep.yaml theme={"dark"}
agents:
- definition: src/agents/agent.yaml
- definition: src/agents/another-agent.yaml
tasks:
- agent_id: "{agents[0].id}"
definition: src/tasks/task.yaml
- agent_id: "{agents[1].id}"
definition: src/tasks/another-task.yaml
tools:
- agent_id: "{agents[0].id}"
definition: src/tools/tool.yaml
- agent_id: "{agents[1].id}"
definition: src/tools/another-tool.yaml
```
The `julep-lock.json` file tracks the state of your project on the Julep platform, mapping local files to their remote counterparts and maintaining relationships between components. This file should be committed to version control to ensure consistent state across team members.
To know more about the schema and the usage of the `julep-lock.json` file, you can read the [Lockfile](https://github.com/julep-ai/julep/tree/dev/cli/README.md#schema-for-julep-lockjson) section.
## Getting Started
To get started with the CLI, follow these steps:
```bash theme={"dark"}
julep auth
```
The `auth` command is used to authenticate with the Julep platform. You need to provide your API key. You can find your API key [here](https://dashboard.julep.ai/).
```bash theme={"dark"}
julep init --template profiling-recommending
```
The `init` command is used to initialize a new Julep project. There are bunch of templates to get you started which you can find [here](https://github.com/julep-ai/library).
```bash theme={"dark"}
julep sync
```
The `sync` command is used to synchronize the local project with the Julep platform. This creates a `julep-lock.json` file which tracks the state of the project on the Julep platform (see [Sync Command](/julepcli/commands#sync) for more details).
Once the project is initialized, you can edit the project in the `julep.yaml` file. Add agents, tasks, tools, etc. in the `src` directory.
```bash theme={"dark"}
julep sync --force-local
```
After editing the project, you can re-sync the project with the Julep platform. This will update the `julep-lock.json` file with the latest state of the project on the Julep platform.
You can use the `--watch` flag to watch the project for changes and re-sync automatically.
```bash theme={"dark"}
julep run --task --input '{"key": "value"}'
```
The `run` command is used to run a task. You can find the task ID in the `julep-lock.json` file.
```bash theme={"dark"}
julep logs --execution-id --tail
```
The `logs` command is used to view the logs of an execution. When you run the `run` command, it will return the execution ID.
You can find more commands in the [Command Reference](/julepcli/commands) section.
## Support
If you need help with further questions in Julep:
* Join our [Discord community](https://discord.com/invite/JTSBGRZrzj)
* Check the [GitHub repository](https://github.com/julep-ai/julep)
* Contact support at [hey@julep.ai](mailto:hey@julep.ai)
# CLI
Source: https://docs.julep.ai/responses/cli
CLI for setting up and using Open Responses API
# Open Responses CLI
A CLI tool for setting up a self-hosted alternative to OpenAI's Responses API. This API lets you create and manage open-ended AI responses for your applications, similar to OpenAI's Responses API, but fully under your control.
## Features
* Easy setup with Docker Compose
* Compatible API endpoints with OpenAI's Responses API
* Management UI for creating, viewing, and managing responses
* Local data storage with PostgreSQL
* Customizable authentication and timeout settings
## Installation
You can install this CLI using Go, npm, or Python:
### Using Go
```bash theme={"dark"}
go install github.com/julep-ai/open-responses@latest
open-responses
```
### Using npm
```bash theme={"dark"}
npx open-responses
```
Or install it globally:
```bash theme={"dark"}
npm install -g open-responses
open-responses
```
Or with bunx:
```bash theme={"dark"}
bunx open-responses
```
### Using Python
```bash theme={"dark"}
pipx install open-responses
pipx open-responses
```
Or install with pip globally:
```bash theme={"dark"}
pip install open-responses
open-responses
```
Or with uv:
```bash theme={"dark"}
uvx open-responses
```
## Usage
### First-time Setup
Before using any commands, you must run the setup command:
```bash theme={"dark"}
open-responses setup
```
This will:
* Ask for configuration settings with default values:
* Host (default: 127.0.0.1)
* Port (default: 8080)
* Docker tag (default: latest\_responses)
* Base Docker Compose URI (default: [https://u.julep.ai/responses-compose.yaml](https://u.julep.ai/responses-compose.yaml))
* Environment file location (default: .env in Git root or current directory)
* API version (default: 0.0.1)
* Ask for API configuration values (port, authentication key, timeout)
* Create a .env file with your settings
* Download or generate a docker-compose.yml file with the necessary services:
* API server
* Database
* Management UI
* Create a configuration file (open-responses.json) to track your settings
The CLI will automatically check for this configuration before running any other commands. If the configuration file doesn't exist, it will prompt you to run the setup command first.
### Configuration File
The CLI stores its configuration in `open-responses.json`, which can be located in:
* The current directory
* The parent directory
* The Git repository root directory
The configuration file tracks:
* All user-defined settings
* Environment variable values
* Creation and update timestamps (both `camelCase` and `snake_case` formats are supported)
* File locations and version information
When you run `setup` again with an existing configuration, it will let you update your settings while preserving your previous values as defaults. If timestamps are missing from an existing configuration, they'll be added automatically when the configuration is updated.
### API Configuration
The API service includes the following configuration options with sensible defaults:
#### Basic Settings
* `HOST`: Host address for the API (default: `127.0.0.1`)
* `PORT`: Port for the UI service (default: `8080`)
* `RESPONSES_API_PORT`: Port for the API service (default: `8080`)
* `DOCKER_TAG`: Docker image tag (default: `latest_responses`)
* `API_VERSION`: API version (default: `0.0.1`)
#### Performance & Limits
* `NODE_ENV`: Node.js environment (default: `production`)
* `LOG_LEVEL`: Logging level (default: `info`)
* `REQUEST_TIMEOUT`: API request timeout in ms (default: `120000` - 2 minutes)
* `MAX_PAYLOAD_SIZE`: Maximum request payload size (default: `10mb`)
* `RATE_LIMIT_WINDOW`: Rate limit window in ms (default: `60000` - 1 minute)
* `RATE_LIMIT_MAX`: Maximum requests per rate limit window (default: `100`)
#### Resource Allocation
The Docker Compose configuration also includes resource limits to ensure stable operation:
* API Service: 1 CPU, 2GB memory (min: 0.25 CPU, 512MB)
* Database: 1 CPU, 1GB memory (min: 0.1 CPU, 256MB)
* Redis: 0.5 CPU, 768MB memory (min: 0.1 CPU, 128MB)
* UI: 0.5 CPU, 512MB memory (min: 0.1 CPU, 128MB)
These settings provide a good balance for most deployments, but you can adjust them in the `docker-compose.yml` file if needed.
### User-Friendly Commands
The CLI provides easy-to-use commands for common operations:
#### Starting the service
```bash theme={"dark"}
open-responses start
```
This user-friendly command:
* Pulls Docker images for your specific architecture
* Starts all services in foreground mode with log streaming
* Automatically stops all services when you press Ctrl+C
* Shows the status of services after startup
* Displays access URLs for the API and admin UI
To run in detached mode (background):
```bash theme={"dark"}
open-responses start --background
```
#### Stopping the service
```bash theme={"dark"}
open-responses stop
```
This command stops all services and performs cleanup (alias for `open-responses compose down`).
#### Checking service status
```bash theme={"dark"}
open-responses status
```
Shows detailed information about all services, including:
* Running state and health status
* Uptime information
* Resource usage summary
* Access URLs
#### Viewing logs
```bash theme={"dark"}
open-responses logs [SERVICE]
```
Shows logs from services with sensible defaults:
* Follows logs in real-time
* Shows colorized output
* Displays last 100 lines by default
* Can target specific services
Examples:
```bash theme={"dark"}
# View logs from all services:
open-responses logs
# View logs from a specific service:
open-responses logs api
```
#### Initializing a new project
```bash theme={"dark"}
open-responses init
```
Creates a new project structure with guided setup:
* Creates directory structure (data, config, logs)
* Generates helpful documentation files
* Runs interactive configuration
* Sets up Docker Compose with best practices
#### Managing API keys
```bash theme={"dark"}
open-responses key
```
Manages API keys for the Responses API service:
```bash theme={"dark"}
# List all API keys (masked):
open-responses key list
# Generate a new API key:
open-responses key generate [type]
# Update an API key:
open-responses key set [value]
```
#### Updating components
```bash theme={"dark"}
open-responses update
```
Updates all components to the latest version:
* Updates Docker Compose configuration
* Pulls latest Docker images
* Backs up your configuration
### Advanced Docker Compose Commands
For more advanced operations, use the compose command group:
```bash theme={"dark"}
open-responses compose [args...]
```
Available commands include:
```bash [expandable] theme={"dark"}
# Start services with additional options:
open-responses compose up [flags]
# Stop and clean up services:
open-responses compose down [flags]
# View logs with custom options:
open-responses compose logs [flags] [SERVICE...]
# List containers:
open-responses compose ps [flags]
# Build services:
open-responses compose build [flags] [SERVICE...]
# Restart services:
open-responses compose restart [flags] [SERVICE...]
# Pull service images:
open-responses compose pull [flags] [SERVICE...]
# Execute commands in containers:
open-responses compose exec [flags] SERVICE COMMAND [ARGS...]
# Run one-off commands:
open-responses compose run [flags] SERVICE COMMAND [ARGS...]
# Validate Docker Compose configuration:
open-responses compose config [flags]
# View processes in containers:
open-responses compose top [SERVICE...]
# Monitor resource usage:
open-responses compose stats [SERVICE...]
```
Each compose command is a direct proxy to the equivalent Docker Compose command and accepts all the same flags and arguments. This provides full access to Docker Compose functionality when needed.
For detailed examples of each command, use the `--help` flag:
```bash theme={"dark"}
open-responses compose up --help
open-responses compose logs --help
```
Or for general help:
```bash theme={"dark"}
open-responses --help
```
## API Endpoints
Once your service is running, the following endpoints will be available:
* `POST /v1/responses` - Create a new response
* `GET /v1/responses/{id}` - Retrieve a response
* `GET /v1/responses` - List all responses
* `DELETE /v1/responses/{id}` - Delete a response
You can access the management UI at `http://localhost:8080` (or your configured port).
## Requirements
* Docker must be installed on your system
* Docker Compose must be installed (either as a standalone binary or integrated plugin)
* Docker Compose V2 (≥ 2.21.0) is recommended for best compatibility
* Docker Compose V1 is supported but with limited functionality
* No other runtime dependencies required (no Node.js or Python needed for running the service)
The CLI will check Docker and Docker Compose requirements and provide helpful instructions if they're not met.
## How it works
This CLI is built with Go and compiled to native binaries for Windows, macOS, and Linux.
When installed via npm or pip, the appropriate binary for your platform is used automatically.
The service itself runs in Docker containers, providing a compatible alternative to OpenAI's Responses API.
## Development
### Project Structure
* `main.go`: Core CLI functionality built with Go
* `open_responses/__init__.py`: Python wrapper for binary distribution
* `scripts/postinstall.js`: Node.js script for platform detection and setup
* `bin/`: Directory for compiled binaries
### Building from Source
Build for your current platform:
```bash theme={"dark"}
npm run build
```
Build for all platforms:
```bash theme={"dark"}
npm run build:all
```
This will generate binaries in the `bin/` directory:
* `bin/open-responses-linux`
* `bin/open-responses-macos`
* `bin/open-responses-win.exe`
### Installing for Development
For Python:
```bash theme={"dark"}
pip install -e .
```
For npm:
```bash theme={"dark"}
npm link
```
### Development Guidelines
This project follows strict formatting and linting guidelines to maintain code quality. We use:
* **Go**: Standard Go formatting with `go fmt`
* **Python**: Ruff for linting and formatting
* **JavaScript**: ESLint and Prettier for linting and formatting
#### Setting Up Development Environment
1. Install development dependencies:
```bash theme={"dark"}
# Install JavaScript dependencies
npm install
# Install Python dependencies
uv pip install ruff
# Install git hooks for automatic linting/formatting
npm run install:hooks
```
2. The git hooks will automatically run formatting and linting checks before each commit.
#### Code Formatting and Linting
You can manually run code formatting and linting using these commands:
```bash theme={"dark"}
# Format all code
npm run format:all
# Lint all code
npm run lint:all
# Format/lint individual languages
npm run format # JavaScript/JSON/Markdown files
npm run py:format # Python files
npm run go:format # Go files
npm run lint # JavaScript files
npm run py:lint # Python files
```
## License
Apache-2.0
# Concepts
Source: https://docs.julep.ai/responses/concepts
Concepts of Julep Open Responses API
## Overview
In this section, we'll cover the key concepts and components of the Julep Responses API. The Julep Responses API is designed to be compatible with OpenAI's interface, making it easy to migrate existing applications that use OpenAI's API to Julep.
* The Open Responses API requires self-hosting. See the [installation guide](/responses/quickstart/#local-installation) below.
* Being in Alpha, the API is subject to change. Check back frequently for updates.
* For more context, see the [OpenAI Responses API](https://platform.openai.com/api-reference/responses) documentation.
## Components
The Responses API offers a streamlined way to interact with language models with the following key components:
* **Response ID**: A unique identifier (`uuid7`) for each response.
* **Model**: The language model used to generate the response (e.g., "claude-3.5-haiku", "gpt-4o", etc.).
* **Input**: The prompt or question sent to the model, which can be simple text or structured input.
* **Output**: The generated content from the model, which can include text, tool outputs, or other structured data.
* **Status**: The current status of the response (completed, failed, in\_progress, incomplete).
* **Tools**: Optional tools that the model can use to enhance its response.
* **Usage**: Token consumption metrics for the response.
### 2.1. Response Configuration Options
When creating a response, you can leverage these configuration options to tailor the experience:
| Option | Type | Description | Default | Status |
| ---------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | --------------------- |
| `model` | `string` | The language model to use (e.g., "claude-3.5-haiku", "gpt-4o"). Check out the [supported models](/integrations/supported-models) for more information. | Required | Implemented |
| `input` | `string` \| `array` | The prompt or structured input to send to the model | Required | Implemented |
| `include` | `array` \| `null` | Types of content to include in the response (e.g., "file\_search\_call.results") | `None` | Partially Implemented |
| `parallel_tool_calls` | `boolean` | Whether to allow tools to be called in parallel | `true` | Implemented |
| `store` | `boolean` | Whether to store the response for later retrieval | `true` | Implemented |
| `stream` | `boolean` | Whether to stream the response as it's generated | `false` | Planned |
| `max_tokens` | `integer` \| `null` | Maximum number of tokens to generate | `None` | Implemented |
| `temperature` | `number` | Controls randomness in response generation (0 to 1) | `1` | Implemented |
| `top_p` | `number` | Controls diversity in token selection (0 to 1) | `1` | Implemented |
| `n` | `integer` \| `null` | Number of responses to generate | `None` | Implemented |
| `stop` | `string` \| `array` \| `null` | Sequence(s) where the model should stop generating | `None` | Implemented |
| `presence_penalty` | `number` \| `null` | Penalty for new tokens based on presence in text so far | `None` | Implemented |
| `frequency_penalty` | `number` \| `null` | Penalty for new tokens based on frequency in text so far | `None` | Implemented |
| `logit_bias` | `object` \| `null` | Modify likelihood of specific tokens appearing | `None` | Implemented |
| `user` | `string` \| `null` | Unique identifier for the end-user | `None` | Implemented |
| `instructions` | `string` \| `null` | Additional instructions to guide the model's response | `None` | Implemented |
| `previous_response_id` | `string` \| `null` | ID of a previous response for context continuity | `None` | Implemented |
| `reasoning` | `object` \| `null` | Controls reasoning effort (low/medium/high) | `None` | Implemented |
| `text` | `object` \| `null` | Configures text format (text or JSON object) | `None` | Implemented |
| `tool_choice` | `"auto"` \| `"none"` \| `object` \| `null` | Controls how the model chooses which tools to use | `None` | Implemented |
| `tools` | `array` \| `null` | List of tools the model can use for generating the response | `None` | Partially Implemented |
| `truncation` | `"disabled"` \| `"auto"` \| `null` | How to handle context overflow | `None` | Planned |
| `metadata` | `object` \| `null` | Additional metadata for the response | `None` | Implemented |
To know more about the roadmap of the Responses API, check out the [Roadmap](/responses/roadmap) page.
## Input Formats
The Responses API supports various input formats to accommodate different use cases:
### Simple Text Input
The simplest way to interact with the Responses API is to provide a text string as input:
```json Simple Text Input theme={"dark"}
{
"input": "What are the top 5 skincare products?"
}
```
### Structured Message Input
For more complex interactions, you can provide a structured array of messages:
```json Structured Message Input theme={"dark"}
{
"input": [
{
"role": "user",
"content": "Please summarize the current market trends in renewable energy."
}
]
}
```
### Multi-modal Input
The Responses API supports multi-modal inputs, allowing you to include images or files along with text:
```json Multi-modal Input theme={"dark"}
{
"input": [
{
"role": "user",
"content": [
{"type": "input_text", "text": "what is in this image?"},
{
"type": "input_image",
"image_url": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
}
]
}
]
}
```
### Tool Usage
The Responses API supports tool usage, allowing the model to perform actions like web searches, function calls, and more to enhance its response.
```json Web Search Tool Definition theme={"dark"}
{
"input": "What are the latest advancements in quantum computing?",
"tools": [
{
"type": "web_search_preview",
"domains": ["https://www.google.com"],
"search_context_size": "small",
"user_location": {
"type": "approximate",
"city": "YOUR_CITY",
"country": "YOUR_COUNTRY",
"region": "YOUR_REGION",
"timezone": "YOUR_TIMEZONE"
}
}
],
}
```
```json Sample Function Tool Call to get the weather [expandable] theme={"dark"}
{
"input": "What's the weather in San Francisco?",
"tools": [
{
"type": "function",
"name": "get_weather",
"description": "Get the current weather in a location",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature to use"
}
},
"required": ["location"]
}
}
],
}
```
## Relationship to Sessions
While [Sessions](/concepts/sessions) provide a persistent, stateful way to interact with agents over multiple turns, the Responses API offers a lightweight, stateless alternative for quick, one-off interactions with language models. Here's how they compare:
| Feature | Sessions | Responses |
| --------------------- | ---------------------------------------------- | --------------------------------------------------------- |
| **State Management** | Maintains conversation history | Stateless (with optional context from previous responses) |
| **Persistence** | Long-lived, for ongoing conversations | Short-lived, for one-off interactions |
| **Agent Integration** | Requires an agent | No agent needed |
| **Setup Complexity** | Requires agent and session creation | Minimal setup (just model and input) |
| **Use Case** | Multi-turn conversations, complex interactions | Quick content generation, processing, or reasoning |
If you need to maintain context across multiple interactions but prefer the simplicity of the Responses API, you can use the `previous_response_id` parameter to link responses together.
## Response Object Structure
The Response object is the core data structure returned by the Julep Responses API as a response to a request. It contains all the information about a generated response. It follows the [OpenAI Responses API](https://platform.openai.com/api-reference/responses). Following is the schema of the Response object:
| Field | Type | Description |
| ---------------------- | ---------------- | ---------------------------------------------------------------------- |
| `id` | string | Unique identifier for the response |
| `object` | string | Always "response" |
| `created_at` | integer | Unix timestamp when the response was created |
| `status` | string | Current status: "completed", "failed", "in\_progress", or "incomplete" |
| `error` | object or null | Error information if the response failed |
| `incomplete_details` | object or null | Details about why a response is incomplete |
| `instructions` | string or null | Optional instructions provided to the model |
| `max_output_tokens` | integer or null | Maximum number of tokens to generate |
| `model` | string | The model used to generate the response |
| `output` | array | List of output items (messages, tool calls, reasoning) |
| `parallel_tool_calls` | boolean | Whether tools can be called in parallel |
| `previous_response_id` | string or null | ID of a previous response for context |
| `reasoning` | object or null | Reasoning steps if reasoning was requested |
| `store` | boolean | Whether the response is stored for later retrieval |
| `temperature` | number | Sampling temperature used (0-1) |
| `text` | object or null | Text formatting options |
| `tool_choice` | string or object | How tools are selected ("auto", "none", "required") |
| `tools` | array | List of tools available to the model |
| `top_p` | number | Top-p sampling parameter (0-1) |
| `truncation` | string | Truncation strategy ("disabled" or "auto") |
| `usage` | object | Token usage statistics |
| `user` | string or null | Optional user identifier |
| `metadata` | object | Custom metadata associated with the response |
The `output` array contains the actual content generated by the model, which can include text messages, tool calls (function, web search, file search, computer), and reasoning items.
## Best Practices
- **1. Be Specific**: Clearly define what you want the model to generate.
- **2. Provide Context**: Include relevant background information in your prompt.
- **3. Use Examples**: When appropriate, include examples of desired outputs in your prompt.
- **1. Match Complexity**: Use more capable models for complex tasks (e.g., reasoning, coding).
- **2. Consider Latency**: Smaller models are faster for simple tasks.
- **3. Test Different Models**: Compare results across models for optimal performance.
- **1. Provide Clear Tool Descriptions**: Help the model understand when and how to use tools.
- **2. Only Include Relevant Tools**: Too many tools can confuse the model's selection process.
- **3. Validate Tool Outputs**: Always verify the information returned from tool calls.
## Next Steps
* [Learn more about the Responses API Examples](/responses/examples) - To learn how to use the Responses API with code examples
* [Learn more about the Julep Sessions](/concepts/sessions) - To explore Julep's stateful conversations when you need ongoing context
* [Learn more about the Open Responses API Roadmap](/responses/roadmap) - To learn more about the Open Responses API Roadmap
* [Learn more about Julep](/introduction/julep) - To learn more about Julep and its features
* [GitHub](https://github.com/julep-ai/julep) - To contribute to the project
# Examples
Source: https://docs.julep.ai/responses/examples
Examples of using Julep Open Responses API
# Open Responses API Examples
Below are practical examples showing how to use the Julep Open Responses API for various use cases.
* The Open Responses API requires self-hosting. See the [installation guide](/responses/quickstart/#local-installation) below.
* Being in Alpha, the API is subject to change. Check back frequently for updates.
* For more context, see the [OpenAI Responses API](https://platform.openai.com/api-reference/responses) documentation.
API Key Configuration
* `RESPONSE_API_KEY` is the API key that you set in the `.env` file.
Model Selection
* While using models other than OpenAI, one might need to add the `provider/` prefix to the model name.
* For supported providers, see the [LiteLLM Providers](https://docs.litellm.ai/providers) documentation.
Environment Setup
* Add the relevant provider keys to the `.env` file to use their respective models.
## Setup
First, set up your environment and create a client:
```python theme={"dark"}
from openai import OpenAI
# Create an OpenAI client pointing to Julep's Open Responses API
client = OpenAI(base_url="http://localhost:8080/", api_key="RESPONSE_API_KEY")
```
## Using Reasoning Features
Enhance your model's reasoning capabilities for solving complex problems:
```python theme={"dark"}
# Create a response with explicit reasoning
reasoning_response = client.responses.create(
model="o1",
input="If Sarah has 3 apples and John has 5, and they combine their apples, then how many apples do they have in total? Explain your approach.",
reasoning={
"effort": "medium" # Control reasoning depth with "low", "medium", or "high"
}
)
# Access the final answer
print(reasoning_response.output_text)
# Output: They would have 8 apples in total. The approach is straightforward: you simply add the number of apples Sarah has (3) to the number of apples John has (5), giving 3 + 5 = 8..
```
## Using Web Search Tool
```python Python theme={"dark"}
web_search_response = openai_client.responses.create(
model="gpt-4o-mini",
tools=[{"type": "web_search_preview"}],
input="What was a positive news story from today?",
)
# The output will include both the text response and any tool calls that were made
```
## Maintaining Conversation History
Create a continuous conversation by referencing previous responses:
```python theme={"dark"}
# Reference a previous response to continue a conversation
follow_up_response = client.responses.create(
model="gpt-4o-mini",
input="What was the final answer?",
previous_response_id=reasoning_response.id
)
print(follow_up_response.output_text)
```
## Retrieving Past Responses
Access previously created responses by their ID:
```python theme={"dark"}
# Retrieve a response by ID
retrieved_response = client.responses.retrieve(response_id="your-response-id")
```
## Setup
First, set up your environment and create an async client:
```python theme={"dark"}
from openai import AsyncOpenAI
from agents import set_default_openai_client
# Create and configure the OpenAI client
custom_client = AsyncOpenAI(base_url="http://localhost:8080/", api_key="RESPONSE_API_KEY")
set_default_openai_client(custom_client)
```
## Creating a Simple Agent
Build a basic agent that can respond to user queries:
```python [expandable] theme={"dark"}
from agents import Agent, Runner
# For Jupyter notebooks:
agent = Agent(
name="Test Agent",
instructions="You are a helpful assistant that provides concise responses.",
model="openrouter/deepseek/deepseek-r1",
)
result = await Runner.run(agent, "Hello! Are you working correctly?")
print(result.final_output)
# For Python scripts, you'd use:
# async def test_installation():
# agent = Agent(
# name="Test Agent",
# instructions="You are a helpful assistant that provides concise responses."
# model="openrouter/deepseek/deepseek-r1",
# )
# result = await Runner.run(agent, "Hello! Are you working correctly?")
# print(result.final_output)
#
# if __name__ == "__main__":
# asyncio.run(test_installation())
# Output: Great to hear! Let me know how I can help you today—whether it's answering questions, solving problems, or just chatting. 😊
```
## Web Search Integration
Create an agent with web search capabilities:
```python [expandable] theme={"dark"}
from agents import Agent, Runner, WebSearchTool
# Create a research assistant with web search capability
research_assistant = Agent(
name="Research Assistant",
instructions="""You are a research assistant that helps users find and summarize information.
When asked about a topic:
1. Search the web for relevant, up-to-date information
2. Synthesize the information into a clear, concise summary
3. Structure your response with headings and bullet points when appropriate
4. Always cite your sources at the end of your response
If the information might be time-sensitive or rapidly changing, mention when the search was performed.
""",
tools=[WebSearchTool()]
)
async def research_topic(topic):
result = await Runner.run(research_assistant, f"Please research and summarize: {topic}. Only return the found links with very minimal text.")
return result.final_output
# Usage example (in Jupyter notebook)
summary = await research_topic("Latest developments in personal productivity apps.")
print(summary)
# Output: Here are some links to the latest developments in personal productivity apps:
# - [10 Hot Productivity Apps for 2024](https://francescod.medium.com/10-hot-productivity-apps-for-2024-e45e68f2ee22) - Medium
# - [The Best Productivity Apps in 2025](https://zapier.com/blog/best-productivity-apps/) - Zapier
# - [The Best Productivity Apps for 2025](https://www.pcmag.com/picks/best-productivity-apps) - PCMag
```
## Custom Function Tools
Create an agent with a custom function tool:
```python [expandable] theme={"dark"}
import os
import requests
from datetime import datetime
from typing import Optional, List
from dataclasses import dataclass
from agents import Agent, Runner, function_tool
from dotenv import load_dotenv
load_dotenv()
@dataclass
class WeatherInfo:
temperature: float
feels_like: float
humidity: int
description: str
wind_speed: float
pressure: int
location_name: str
rain_1h: Optional[float] = None
visibility: Optional[int] = None
@function_tool
def get_weather(lat: float, lon: float) -> str:
"""Get the current weather for a specified location using OpenWeatherMap API.
Args:
lat: Latitude of the location (-90 to 90)
lon: Longitude of the location (-180 to 180)
"""
# Get API key from environment variables
WEATHER_API_KEY = os.getenv("OPENWEATHERMAP_API_KEY")
# Build URL with parameters
url = f"https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={WEATHER_API_KEY}&units=metric"
try:
response = requests.get(url)
response.raise_for_status()
data = response.json()
# Extract weather data from the response
weather_info = WeatherInfo(
temperature=data["main"]["temp"],
feels_like=data["main"]["feels_like"],
humidity=data["main"]["humidity"],
description=data["weather"][0]["description"],
wind_speed=data["wind"]["speed"],
pressure=data["main"]["pressure"],
location_name=data["name"],
visibility=data.get("visibility"),
rain_1h=data.get("rain", {}).get("1h"),
)
# Build the response string
weather_report = f"""
Weather in {weather_info.location_name}:
- Temperature: {weather_info.temperature}°C (feels like {weather_info.feels_like}°C)
- Conditions: {weather_info.description}
- Humidity: {weather_info.humidity}%
- Wind speed: {weather_info.wind_speed} m/s
- Pressure: {weather_info.pressure} hPa
"""
return weather_report
except requests.exceptions.RequestException as e:
return f"Error fetching weather data: {str(e)}"
```
## Using the Tool with an Agent
Create an agent that uses the custom function tool:
```python [expandable] theme={"dark"}
# Create a weather assistant
weather_assistant = Agent(
name="Weather Assistant",
instructions="""You are a weather assistant that can provide current weather information.
When asked about weather, use the get_weather tool to fetch accurate data.
If the user doesn't specify a country code and there might be ambiguity,
ask for clarification (e.g., Paris, France vs. Paris, Texas).
Provide friendly commentary along with the weather data, such as clothing suggestions
or activity recommendations based on the conditions.
""",
tools=[get_weather]
)
async def main():
runner = Runner()
simple_request = await runner.run(weather_assistant, "What are your capabilities?")
request_with_location = await runner.run(weather_assistant, "What's the weather like in Tashkent right now?")
print(simple_request.final_output)
print("-"*70)
print(request_with_location.final_output)
await main()
# Output:
# I'm a weather assistant that can provide you with current weather information. If you ask about the weather for a specific location, I can use a tool to fetch accurate and up-to-date weather data for that place.
#
# Additionally, I can offer friendly suggestions based on the weather, like what to wear or what activities might be suitable. If the location you mention is ambiguous (like Paris, which could be in France or Texas), I might ask for clarification to ensure I provide you with the correct weather information.
# ----------------------------------------------------------------------
# Right now in Tashkent, it's a bit cool with a temperature of 8.84°C, feeling slightly cooler at 7.06°C. The sky is overcast with lots of clouds, and the humidity is at 76%, so you might feel a bit of dampness in the air. There's a gentle breeze with the wind blowing at 3.09 m/s.
#
# With these conditions, I'd recommend wearing a warm jacket if you're heading out. It's a great day to enjoy indoor activities or perhaps a warm drink at a cozy café! Stay comfortable!
```
## Next Steps
You've got Open Responses running – here's what to explore next:
* [Learn more about the Open Responses API Concepts](/responses/concepts) – To learn more about core concepts and how Open Responses structures these building blocks in your applications.
* [Learn more about the Open Responses API Roadmap](/responses/roadmap) – To see upcoming features.
* [OpenAI's Responses API Documentation](https://platform.openai.com/api-reference/responses) - For more insight into the original API that inspired Julep's Responses
* [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) - Explore OpenAI's Agents SDK that works with Julep's Open Responses API
* [Learn more about Julep](/introduction/julep) - To learn more about Julep and its features
* [Julep's GitHub Repository](https://github.com/julep-ai/julep) - To contribute to the project
# Quickstart
Source: https://docs.julep.ai/responses/quickstart
Get started with Julep Open Responses API for LLM interactions
## Introduction
Julep's Open Responses is a self-hosted, open-source implementation of OpenAI's Responses API that works with any LLM backend. It provides a lightweight interface for generating content with Large Language Models (LLMs) without needing to create persistent agents or sessions.
To try it out, just run `npx -y open-responses init` (or `uvx`) and that's it! :)
### What is Open Responses?
Julep's Open Responses lets you run your own server that is compatible with OpenAI's Responses API, while giving you the freedom to use alternative models like:
* Anthropic's Claude
* Alibaba's Qwen
* Deepseek R1
* and many others ...
It's essentially a drop-in replacement that you control, with a permissive Apache-2.0 license. As an early release, we welcome your feedback and contributions to help improve it.
### Why Open Responses?
* **Model Flexibility**: Use any LLM backend without vendor lock-in, including local model deployment
* **Self-hosted & Private**: Maintain full control over your deployment on your own infrastructure (cloud or on-premise)
* **Drop-in Compatibility**: Seamlessly integrates with the official Agents SDK by simply pointing to your self-hosted URL
* **Easy Deployment**: Quick setup via docker-compose or our CLI with minimal configuration
* **Built-in Tools**: Automatic execution of tool calls (like web\_search) using open & pluggable alternatives
- The Open Responses API requires self-hosting. See the [installation guide](#local-installation) below.
- Being in Alpha, the API is subject to change. Check back frequently for updates.
- For more context, see the [OpenAI Responses API](https://platform.openai.com/api-reference/responses) documentation.
## Local Installation
This section will guide you through the steps to set up the Julep's Open Responses API.
### Prerequisites
Install [Docker](https://docs.docker.com/get-docker/)
### Installation
The Julep's Open Responses API is a fully microservice-based architecture. It is fully dockerized and can be easily deployed on any infrastructure that supports Docker. There are two ways to install the API:
* [Docker Installation](#docker-installation)
* [CLI Installation](#cli-installation)
#### Docker Installation
```bash theme={"dark"}
mkdir julep-responses-api
```
```bash theme={"dark"}
cd julep-responses-api
```
```bash theme={"dark"}
wget https://u.julep.ai/responses-env.example -O .env
```
Edit the `.env` file with your own values.
```bash theme={"dark"}
wget https://u.julep.ai/responses-compose.yaml -O docker-compose.yml
```
Download the file to the current directory with the name `docker-compose.yml`. This is the file that will be used to run the Docker containers.
```bash theme={"dark"}
docker compose up --watch
```
This will start the containers in watch mode.
```bash theme={"dark"}
docker ps
```
#### CLI Installation
The CLI is a lightweight alternative to Docker for those who prefer not to use Docker directly.
Internally, it uses Docker to run the containers.
You can install the CLI using several package managers:
```bash npx theme={"dark"}
# Using npx directly
npx open-responses
# Or install globally
npm install -g open-responses
```
```bash uv theme={"dark"}
# Install using uv
uvx open-responses
# Install using pip globally
pip install open-responses
open-responses
```
```bash npx theme={"dark"}
npx open-responses setup
```
```bash uv theme={"dark"}
uvx open-responses setup
```
Before using any commands, you must run the setup command
```bash npx theme={"dark"}
npx open-responses start
```
```bash uv theme={"dark"}
uvx open-responses start
```
This will start the API in watch mode
To learn more about the CLI one can use the checkout the [CLI Documentation](/responses/cli).
## Quickstart Example
With the OpenAI SDK initialized, you can now use the Responses API to generate content.
API Key Configuration
* `RESPONSE_API_KEY` is the API key that you set in the `.env` file.
Model Selection
* While using models other than OpenAI, one might need to add the `provider/` prefix to the model name.
* For supported providers, see the [LiteLLM Providers](https://docs.litellm.ai/providers) documentation.
Environment Setup
* Add the relevant provider keys to the `.env` file to use their respective models.
### 1. Install the OpenAI SDK
```bash pip theme={"dark"}
pip install openai
```
```bash npm theme={"dark"}
npm install openai
```
### 2. Initialize the OpenAI client
```python Python theme={"dark"}
from openai import OpenAI
openai_client = OpenAI(base_url="http://localhost:8080/", api_key="RESPONSE_API_KEY")
```
```javascript Node.js theme={"dark"}
import { OpenAI } from 'openai';
const openai_client = new OpenAI({ baseURL: 'http://localhost:8080/', apiKey: 'RESPONSE_API_KEY' });
```
### 3. Generate a response
```python Python theme={"dark"}
import os
from openai import OpenAI
openai_client = OpenAI(base_url="http://localhost:8080/", api_key=os.getenv("RESPONSE_API_KEY"))
response = openai_client.responses.create(
model="gpt-4o-mini",
input="How many people live in the world?"
)
print("Generated response:", response.output[0].content[0].text)
```
```javascript Node.js theme={"dark"}
import { OpenAI } from 'openai';
const openai_client = new OpenAI({ baseURL: 'http://localhost:8080/', apiKey: "RESPONSE_API_KEY" });
const response = await openai_client.responses.create({
model: "gpt-4o-mini",
input: "How many people live in the world?"
});
console.log("Generated response:", response.output[0].content[0].text);
```
## Next Steps
You've got Open Responses running – here's what to explore next:
* [Learn more about the Open Responses API Examples](/responses/examples) – To learn how to use the Responses API with code examples
* [Learn more about the Open Responses API Roadmap](/responses/roadmap) – To see upcoming features including:
* [Learn more about Julep](/introduction/julep) - To learn more about Julep and its features
* [GitHub](https://github.com/julep-ai/julep) - To contribute to the project
# Roadmap
Source: https://docs.julep.ai/responses/roadmap
Roadmap for the Julep Open Responses API
## Overview
The Julep's Open Responses API is under active development. This page provides a comprehensive overview of implemented features, in-progress development, and planned enhancements.
* The Open Responses API requires self-hosting. See the [installation guide](/responses/quickstart/#local-installation) below.
* Being in Alpha, the API is subject to change. Check back frequently for updates.
* For more context, see the [OpenAI Responses API](https://platform.openai.com/api-reference/responses) documentation.
## Current Implementation Status
### 1. Implemented Features ✅
This section lists the core functionalities that have been implemented.
#### 1.1. Core Functionality
* Basic Response Creation and Retrieval
* Text and Image Input Support
* Multiple Input and Output Formats
* Response Metadata
* Reasoning Support
* Tool Choice Configuration
#### 1.2. Advanced Features
* **Function Calling**
* Dynamic function execution
* Parameter validation
* Error handling
* Function chaining support
* **Web Search Tool Support**
* Real-time web queries
* Result filtering and ranking
* Source verification
* Search result caching
* **Response Metadata**
* Creation timestamps
* Processing duration
* Resource usage metrics
* Performance analytics
* **Reasoning Support**
* Step-by-step reasoning
* Logical deduction
* Context awareness
* Decision tracking
* **Tool Choice Configuration**
* Tool selection criteria
* Priority-based routing
* Fallback mechanisms
* Tool-specific parameters
### 2. Partially Implemented Features 🔄
This section lists the features that are partially implemented.
#### 2.1. File Search Tool
* Basic file indexing
* Full text search across indexed files
* Support for multiple file formats, including PDFs and office documents
* Relevance-based ranking of search results
* Filters by file type, date, and metadata
* Indexing improvements for faster queries
#### 2.2. Reasoning Effort
* Baseline reasoning budget allocation
* Dynamic budget adjustment based on request complexity
* Optimized compute usage for long running tasks
* Performance monitoring
#### 2.3. Annotations for Citations
* Basic citation format
* Automatic source verification
* Customizable citation styles
* Auto-generation of citations from references
### 3. Planned Features 🔜
This section lists the features that are planned to be implemented in the future.
#### 3.1. Streaming Support
* Real-time response streaming
* Progress indicators
* Partial response handling
* Connection management
#### 3.2. Response Deletion Endpoint
* Secure deletion
* Cascade deletion options
* Deletion confirmation
* Audit logging
#### 3.3. Integration Features
* **Computer Tool Call Support**
* OpenAI compatibility layer
* Tool translation service
* Error handling
* Response mapping
#### 3.4. Performance Optimizations
* **Truncation Auto Support**
* Smart content truncation
* Context preservation
* Length optimization
* Quality maintenance
#### 3.5. API Enhancements
* **GET /responses//input\_items**
* Input item retrieval
* Item metadata
* Version history
* Relationship mapping
* **Pagination for Input Items**
* Page size configuration
* Cursor-based navigation
* Total count information
* Filtering options
### 4. Future Considerations
This section lists the features that are planned to be implemented in the future.
#### 4.1. System Enhancements
* Enhanced security features
* Advanced analytics dashboard
* Custom plugin system
* Multi-language support
#### 4.2. Performance & Scaling
* Rate limiting and quotas
* Advanced caching mechanisms
* Load balancing improvements
* Distributed processing support
## Next Steps
* [Return to the Responses API Quickstart](/responses/quickstart) - To get started with the Responses API
* [Return to the Responses API Examples](/responses/examples) - To learn through practical examples
* [Return to the Responses API Concepts](/responses/concepts) - To understand core concepts and components
* [Learn more about Julep](/introduction/julep) - To learn more about Julep and its features
* [GitHub](https://github.com/julep-ai/julep) - To contribute to the project
# Authentication
Source: https://docs.julep.ai/sdks/common/authentication
Authentication patterns for Julep SDKs
# Authentication
Learn about different authentication methods and best practices when using Julep SDKs.
## API Key Authentication
### Basic Usage
```python theme={"dark"}
from julep import Client
client = Client(api_key="your_api_key")
```
### Environment Variables
```python theme={"dark"}
import os
from julep import Client
client = Client(api_key=os.environ.get("JULEP_API_KEY"))
```
## Advanced Authentication
### Custom Authentication Headers
```python theme={"dark"}
client = Client(
api_key="your_api_key",
headers={
"X-Custom-Header": "value"
}
)
```
### Multiple Environments
```python theme={"dark"}
class JulepConfig:
def __init__(self, environment="production"):
self.api_key = self._get_api_key(environment)
self.base_url = self._get_base_url(environment)
def create_client(self):
return Client(
api_key=self.api_key,
base_url=self.base_url
)
```
## Security Best Practices
1. Never hardcode API keys in your code
2. Use environment variables or secure vaults
3. Rotate API keys periodically
4. Use different API keys for different environments
5. Implement proper key management policies
# Error Handling
Source: https://docs.julep.ai/sdks/common/error-handling
Best practices for handling errors in Julep SDKs
# Error Handling
Learn how to effectively handle errors and exceptions when using Julep SDKs.
## Common Error Types
### API Errors
```python theme={"dark"}
try:
agent = client.agents.create(name="Test Agent")
except julep.APIError as e:
print(f"API Error: {e.status_code} - {e.message}")
```
### Authentication Errors
```python theme={"dark"}
try:
client = julep.Client(api_key="invalid_key")
except julep.AuthenticationError as e:
print(f"Auth failed: {e}")
```
### Rate Limit Errors
```python theme={"dark"}
try:
results = client.agents.list()
except julep.RateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after} seconds")
```
## Error Handling Patterns
### Retrying Failed Requests
```python theme={"dark"}
from julep.utils import retry_with_backoff
@retry_with_backoff(max_retries=3)
def create_agent_with_retry():
return client.agents.create(name="Test Agent")
```
### Graceful Degradation
```python theme={"dark"}
def get_agent_safely(agent_id):
try:
return client.agents.get(agent_id)
except julep.NotFoundError:
return create_default_agent()
except julep.APIError:
return use_cached_agent(agent_id)
```
## Best Practices
1. Always wrap API calls in try-except blocks
2. Handle specific exceptions before generic ones
3. Implement retry logic for transient failures
4. Log errors with appropriate context
5. Provide meaningful error messages to users
# Common Secrets Patterns
Source: https://docs.julep.ai/sdks/common/secrets
Common patterns for working with secrets across SDKs
# Common Secrets Patterns
This guide covers common patterns and best practices for working with secrets that apply across all Julep SDKs.
## Secret Management Lifecycle
The typical lifecycle for secrets in Julep applications includes:
1. **Creation**: Establishing new secrets
2. **Retrieval**: Accessing secret metadata (not values)
3. **Usage**: Referencing secrets in tasks and tools
4. **Update**: Rotating or changing secret values
5. **Deletion**: Removing secrets when no longer needed
## Naming Conventions
Consistent naming helps with secret organization:
* Use snake\_case formatting (e.g., `aws_access_key`)
* Be descriptive but concise
* Include service name as prefix (`stripe_secret_key` vs just `secret_key`)
* For multiple environments, include environment prefix (`dev_stripe_key`, `prod_stripe_key`)
## Secret Reference Patterns
When using secrets in tasks, you have several reference patterns available:
### Direct Reference
Reference a secret directly by name:
```yaml theme={"dark"}
secret_name: openai_api_key
```
### Multiple Secrets
For operations requiring multiple secrets:
```yaml theme={"dark"}
secrets:
service_api_key: "api_key_secret_name"
service_auth_token: "auth_token_secret_name"
```
### Expression Reference
Reference secrets within expressions:
```yaml theme={"dark"}
arguments:
headers:
Authorization: "$ f'Bearer {secrets.api_token}'"
```
### LLM Provider Keys
Store LLM API keys with standard names for automatic lookup:
```python theme={"dark"}
# Python SDK
client.secrets.create(
name="OPENAI_API_KEY",
value="sk-..."
)
# Node.js SDK
await julep.secrets.create({
name: 'ANTHROPIC_API_KEY',
value: 'sk-ant-...'
});
```
## Error Handling
Common error scenarios when working with secrets:
1. **Secret Not Found**: The referenced secret doesn't exist
2. **Permission Denied**: No access to the requested secret
3. **Validation Error**: Secret name doesn't match required format
4. **Duplicate Name**: Attempting to create a secret with a name that already exists
Handle these consistently across your application:
```python theme={"dark"}
# Python SDK
from julep.exceptions import SecretNotFoundError, ValidationError
try:
secret = client.secrets.get(name="non_existent_secret")
except SecretNotFoundError:
# Handle missing secret
print("Secret not found, using default value")
except ValidationError as e:
# Handle validation error
print(f"Invalid secret name: {e}")
```
```javascript theme={"dark"}
// Node.js SDK
try {
const secret = await julep.secrets.get({ name: 'non_existent_secret' });
} catch (error) {
if (error.code === 'not_found') {
// Handle missing secret
console.log('Secret not found, using default value');
} else if (error.code === 'validation_error') {
// Handle validation error
console.log(`Invalid secret name: ${error.message}`);
}
}
```
## Testing with Secrets
For testing applications that use secrets:
1. Create a separate set of test secrets with appropriate prefixes
2. Use mocking in unit tests to avoid requiring real secrets
3. For integration tests, use dedicated test accounts and credentials
4. Never use production secrets in test environments
Example of mocking secrets for testing:
```python theme={"dark"}
# Python mock example
import pytest
from unittest.mock import patch
@pytest.fixture
def mock_secrets():
return {
"api_key": "mock-api-key-123",
"auth_token": "mock-token-456"
}
@patch("julep.client.Secrets.get")
def test_with_mock_secrets(mock_get, mock_secrets):
mock_get.return_value = mock_secrets
# Test code that uses secrets
```
```javascript theme={"dark"}
// JavaScript mock example
jest.mock('@julep/sdk', () => {
return {
Julep: jest.fn().mockImplementation(() => {
return {
secrets: {
get: jest.fn().mockResolvedValue({
name: 'api_key',
value: 'mock-api-key-123'
})
}
};
})
};
});
```
## Migrating from Environment Variables
When migrating from environment variables to Julep secrets:
1. Create a list of all environment variables used in your application
2. Create corresponding secrets in Julep with the same names
3. Update your code to reference Julep secrets instead of environment variables
4. Validate functionality before removing the original environment variables
Migration script example:
```python [expandable] theme={"dark"}
import os
from julep import Julep
client = Julep(api_key="your_api_key")
# List of environment variables to migrate
env_vars_to_migrate = [
"OPENAI_API_KEY",
"STRIPE_SECRET_KEY",
"DATABASE_URL",
"AUTH_TOKEN"
]
# Migrate each environment variable to a Julep secret
for env_var in env_vars_to_migrate:
value = os.environ.get(env_var)
if value:
try:
client.secrets.create(
name=env_var.lower(), # Convert to snake_case
value=value,
description=f"Migrated from environment variable {env_var}"
)
print(f"Successfully migrated {env_var} to Julep secret")
except Exception as e:
print(f"Failed to migrate {env_var}: {e}")
else:
print(f"Environment variable {env_var} not found")
```
## Integration with External Secret Managers
For organizations using external secret managers, you can sync to Julep:
```python [expandable] theme={"dark"}
# Example syncing AWS Secrets Manager to Julep
import boto3
from julep import Julep
# Initialize clients
julep_client = Julep(api_key="your_api_key")
aws_client = boto3.client('secretsmanager')
# Get secrets from AWS
response = aws_client.list_secrets()
for secret in response['SecretList']:
# Get the secret value
secret_value = aws_client.get_secret_value(SecretId=secret['ARN'])
# Create or update the secret in Julep
try:
julep_client.secrets.create(
name=f"aws_{secret['Name']}",
value=secret_value['SecretString'],
description=f"Synced from AWS Secrets Manager: {secret['Name']}",
metadata={
"source": "aws",
"arn": secret['ARN'],
"sync_date": datetime.now().isoformat()
}
)
print(f"Synced secret {secret['Name']}")
except Exception:
# Secret already exists, update it
julep_client.secrets.update(
name=f"aws_{secret['Name']}",
value=secret_value['SecretString'],
metadata={
"source": "aws",
"arn": secret['ARN'],
"sync_date": datetime.now().isoformat()
}
)
print(f"Updated secret {secret['Name']}")
```
## Security Best Practices
1. Limit who has access to create and manage secrets
2. Never log secret values, even in debug environments
3. Rotate secrets regularly, especially for high-value credentials
4. Use the most specific scope possible for each secret
5. Audit secret usage and access patterns
6. Use metadata to track important information about secrets
7. Implement an encrypted backup strategy for critical secrets
## Next Steps
* [Using Secrets in Julep](/guides/using-secrets) - Step-by-step guide for using secrets
* [Secrets Management](/advanced/secrets-management) - Advanced guide for managing secrets
* [API Reference](/api-reference#tag/secrets) - Complete API reference for secrets
# Testing
Source: https://docs.julep.ai/sdks/common/testing
Testing patterns and best practices for Julep SDK implementations
# Testing
Learn how to effectively test your Julep SDK implementations.
## Unit Testing
### Mocking API Responses
```python theme={"dark"}
from unittest.mock import patch
def test_agent_creation():
with patch('julep.Client') as MockClient:
mock_client = MockClient()
mock_client.agents.create.return_value = {
"id": "test_id",
"name": "Test Agent"
}
agent = mock_client.agents.create(name="Test Agent")
assert agent["name"] == "Test Agent"
```
### Testing Error Handling
```python theme={"dark"}
def test_api_error_handling():
with patch('julep.Client') as MockClient:
mock_client = MockClient()
mock_client.agents.get.side_effect = julep.APIError(
message="Not found",
status_code=404
)
with pytest.raises(julep.APIError):
mock_client.agents.get("nonexistent_id")
```
## Integration Testing
### Test Client Setup
```python theme={"dark"}
import pytest
@pytest.fixture
def test_client():
return Client(
api_key="test_key",
base_url="https://test-api.julep.ai"
)
def test_end_to_end(test_client):
agent = test_client.agents.create(name="Test Agent")
assert agent.id is not None
```
## Best Practices
1. Use test fixtures for common setup
2. Mock external API calls in unit tests
3. Use integration tests for end-to-end validation
4. Test error cases and edge conditions
5. Maintain a comprehensive test suite
# Introduction
Source: https://docs.julep.ai/sdks/index
Getting started with Julep SDKs for Python and Node.js
The Julep SDKs provide powerful interfaces to interact with Julep's AI agent platform. These SDKs allow you to create, manage, and execute AI agents, tasks, and sessions directly from your applications.
## Installation
```bash Python theme={"dark"}
# Install using pip
pip install julep-sdk
# Or using poetry
poetry add julep-sdk
```
```bash Node.js theme={"dark"}
# Install using npm
npm install @julep/sdk
# Or using yarn
yarn add @julep/sdk
# Or using bun
bun add @julep/sdk
```
## Basic Usage
```python Python [expandable] theme={"dark"}
from julep import Julep
# Initialize the client
client = Julep(
api_key='your_api_key',
environment='production' # or 'development'
)
# Create an agent
agent = client.agents.create(
name='My First Agent',
model='claude-3.5-sonnet',
about='A helpful AI assistant'
)
# Create a task
task = client.tasks.create(
agent_id=agent.id,
name='Simple Task',
description='A basic task example',
main=[
{
'prompt': 'Hello! How can I help you today?'
}
]
)
# Execute the task
execution = client.executions.create(task_id=task.id)
```
```javascript Node.js [expandable] theme={"dark"}
const { Julep } = require('@julep/sdk');
// Initialize the client
const client = new Julep({
apiKey: 'your_api_key',
environment: 'production' // or 'development'
});
// Create an agent
const agent = await client.agents.create({
name: 'My First Agent',
model: 'claude-3.5-sonnet',
about: 'A helpful AI assistant'
});
// Create a task
const task = await client.tasks.create(agent.id, {
name: 'Simple Task',
description: 'A basic task example',
main: [
{
prompt: 'Hello! How can I help you today?'
}
]
});
// Execute the task
const execution = await client.executions.create(task.id);
```
## Features
* **Comprehensive API Coverage**: Access all Julep platform features
* **Type Safety**: Built with TypeScript (Node.js) and type hints (Python)
* **Modern Async Support**: Promise-based interface in Node.js and optional async support in Python
* **Error Handling**: Detailed error information and handling
* **Automatic Retries**: Built-in retry mechanism for failed requests
## SDK Documentation
Get started with the Python SDK
Get started with the Node.js SDK
## Core Components
Learn how to create and manage AI agents with Python
Create and execute complex AI workflows in Python
Manage stateful conversations with agents using Python
Extend your agents with tools and integrations in Python
Learn how to create and manage AI agents with Node.js
Create and execute complex AI workflows in Node.js
Manage stateful conversations with agents using Node.js
Extend your agents with tools and integrations in Node.js
## Additional Resources
Complete API reference documentation
Sample code and use cases
Learn advanced patterns and best practices
# Advanced Usage
Source: https://docs.julep.ai/sdks/nodejs/advanced-usage
Advanced patterns and best practices for the Node.js SDK
This guide covers advanced usage patterns and best practices for the Julep Node.js SDK.
## Parallel Task Execution
Execute multiple tasks in parallel for better performance:
```javascript [expandable] theme={"dark"}
// Create multiple tasks
const tasks = await Promise.all([
client.tasks.create(agentId, {
name: 'Task 1',
main: [/* ... */]
}),
client.tasks.create(agentId, {
name: 'Task 2',
main: [/* ... */]
})
]);
// Execute tasks in parallel
const executions = await Promise.all(
tasks.map(task => client.executions.create(task.id))
);
// Wait for all executions to complete
const results = await Promise.all(
executions.map(async execution => {
let status;
do {
status = await client.executions.get(execution.id);
await new Promise(resolve => setTimeout(resolve, 1000));
} while (status.status !== 'succeeded' && status.status !== 'failed');
return status;
})
);
```
## Custom Error Handling
Implement robust error handling with retries:
```javascript [expandable] theme={"dark"}
class RetryableError extends Error {
constructor(message, retryAfter = 1000) {
super(message);
this.name = 'RetryableError';
this.retryAfter = retryAfter;
}
}
async function withRetry(fn, maxRetries = 3, initialDelay = 1000) {
let lastError;
let delay = initialDelay;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (error.name === 'ValidationError') {
throw error; // Don't retry validation errors
}
if (error.status === 429) { // Rate limit
delay = error.retryAfter || delay * 2;
} else if (error.status >= 500) { // Server error
delay = delay * 2;
} else {
throw error; // Don't retry other errors
}
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
// Usage example
const createAgentWithRetry = async () => {
return await withRetry(async () => {
return await client.agents.create({
name: 'Resilient Agent',
model: 'claude-3.5-sonnet'
});
});
};
```
## Event Streaming
Handle real-time updates from task executions:
```javascript [expandable] theme={"dark"}
const { EventEmitter } = require('events');
class ExecutionStream extends EventEmitter {
constructor(client, executionId, pollInterval = 1000) {
super();
this.client = client;
this.executionId = executionId;
this.pollInterval = pollInterval;
this.isRunning = false;
}
async start() {
this.isRunning = true;
while (this.isRunning) {
try {
const status = await this.client.executions.get(this.executionId);
this.emit('update', status);
if (status.status === 'succeeded' || status.status === 'failed') {
this.isRunning = false;
this.emit('end', status);
break;
}
await new Promise(resolve => setTimeout(resolve, this.pollInterval));
} catch (error) {
this.emit('error', error);
this.isRunning = false;
break;
}
}
}
stop() {
this.isRunning = false;
}
}
// Usage example
const stream = new ExecutionStream(client, executionId);
stream.on('update', status => {
console.log('Execution status:', status.status);
});
stream.on('end', status => {
console.log('Execution completed:', status);
});
stream.on('error', error => {
console.error('Execution error:', error);
});
stream.start();
```
## Batch Processing
Process large amounts of data efficiently:
```javascript [expandable] theme={"dark"}
async function processBatch(items, batchSize = 10) {
const batches = [];
for (let i = 0; i < items.length; i += batchSize) {
batches.push(items.slice(i, i + batchSize));
}
const results = [];
for (const batch of batches) {
const batchResults = await Promise.all(
batch.map(async item => {
try {
const execution = await client.executions.create(taskId, {
input: { item }
});
return { item, execution };
} catch (error) {
return { item, error };
}
})
);
results.push(...batchResults);
// Optional: Add delay between batches
await new Promise(resolve => setTimeout(resolve, 1000));
}
return results;
}
// Usage example
const items = ['item1', 'item2', 'item3', /* ... */];
const results = await processBatch(items, 5);
```
## Custom Task Middleware
Add custom middleware to task executions:
```javascript [expandable] theme={"dark"}
class TaskMiddleware {
constructor(client) {
this.client = client;
this.middlewares = [];
}
use(fn) {
this.middlewares.push(fn);
return this;
}
async execute(taskId, input) {
let execution = await this.client.executions.create(taskId, { input });
for (const middleware of this.middlewares) {
execution = await middleware(execution, this.client);
}
return execution;
}
}
// Usage example
const middleware = new TaskMiddleware(client);
// Add logging middleware
middleware.use(async (execution, client) => {
console.log(`Execution ${execution.id} started`);
const result = await client.executions.get(execution.id);
console.log(`Execution ${execution.id} completed:`, result.status);
return result;
});
// Add error handling middleware
middleware.use(async (execution, client) => {
try {
return await client.executions.get(execution.id);
} catch (error) {
console.error(`Execution ${execution.id} failed:`, error);
throw error;
}
});
// Execute task with middleware
const result = await middleware.execute(taskId, { data: 'test' });
```
## Advanced Session Management
Implement sophisticated session management:
```javascript [expandable] theme={"dark"}
class SessionManager {
constructor(client) {
this.client = client;
this.sessions = new Map();
}
async getOrCreateSession(userId, agentId) {
if (this.sessions.has(userId)) {
const session = this.sessions.get(userId);
try {
await this.client.sessions.get(session.id);
return session;
} catch (error) {
this.sessions.delete(userId);
}
}
const session = await this.client.sessions.create({
user_id: userId,
agent_id: agentId,
context_overflow: 'adaptive'
});
this.sessions.set(userId, session);
return session;
}
async chat(userId, message) {
const session = await this.getOrCreateSession(userId);
return await this.client.sessions.chat(session.id, {
messages: [{ role: 'user', content: message }]
});
}
async cleanup(maxAge = 24 * 60 * 60 * 1000) {
const now = Date.now();
for (const [userId, session] of this.sessions) {
if (now - new Date(session.created_at).getTime() > maxAge) {
await this.client.sessions.delete(session.id);
this.sessions.delete(userId);
}
}
}
}
// Usage example
const sessionManager = new SessionManager(client);
// Chat with automatic session management
const response = await sessionManager.chat(userId, 'Hello!');
// Cleanup old sessions
await sessionManager.cleanup();
```
## Next Steps
Complete API documentation
Real-world examples
Common error patterns
Testing strategies
# Agents
Source: https://docs.julep.ai/sdks/nodejs/agents
Create and manage AI agents with the Node.js SDK
Agents are the core building blocks in Julep. They are AI-powered entities that can execute tasks and interact with users through sessions.
## Creating an Agent
```javascript theme={"dark"}
const agent = await client.agents.create({
name: 'Customer Support Agent',
model: 'claude-3.5-sonnet',
about: 'A helpful customer support agent that assists users with their queries',
metadata: {
department: 'support',
language: 'english'
}
});
```
## Retrieving Agents
```javascript theme={"dark"}
// Get a specific agent
const agent = await client.agents.get(agentId);
// List all agents
const agents = await client.agents.list({
limit: 10,
offset: 0
});
// Search agents
const searchResults = await client.agents.search({
query: 'support',
metadata: {
department: 'support'
}
});
```
## Updating Agents
```javascript theme={"dark"}
const updatedAgent = await client.agents.update(agentId, {
name: 'Senior Support Agent',
metadata: {
department: 'support',
seniority: 'senior'
}
});
```
## Deleting Agents
```javascript theme={"dark"}
await client.agents.delete(agentId);
```
## Managing Agent Documents
Agents can be associated with documents that provide context for their tasks:
```javascript theme={"dark"}
// Add a document
const document = await client.agents.docs.create(agentId, {
title: 'Support Guidelines',
content: 'Here are the guidelines for customer support...',
metadata: {
category: 'guidelines',
version: '1.0'
}
});
// Search documents
const docs = await client.agents.docs.search(agentId, {
query: 'refund policy',
metadata: {
category: 'policy'
}
});
// Delete a document
await client.agents.docs.delete(agentId, documentId);
```
## Adding Tools to Agents
Extend your agent's capabilities by adding tools:
```javascript [expandable] theme={"dark"}
// Add a web search tool
const tool = await client.agents.tools.create(agentId, {
name: 'web_search',
description: 'Search the web for information',
integration: {
provider: 'brave',
method: 'search',
setup: {
brave_api_key: process.env.BRAVE_API_KEY
}
}
});
// Add a custom function tool
const customTool = await client.agents.tools.create(agentId, {
name: 'calculate_price',
description: 'Calculate the final price including tax',
type: 'function',
function: {
parameters: {
type: 'object',
properties: {
base_price: {
type: 'number',
description: 'Base price before tax'
},
tax_rate: {
type: 'number',
description: 'Tax rate as a decimal'
}
},
required: ['base_price', 'tax_rate']
}
}
});
```
## Error Handling
The SDK uses custom error classes for better error handling:
```javascript theme={"dark"}
try {
const agent = await client.agents.create({
name: 'Test Agent',
model: 'invalid-model'
});
} catch (error) {
if (error.name === 'ValidationError') {
console.error('Invalid model specified:', error.message);
} else if (error.name === 'ApiError') {
console.error('API error:', error.message, error.status);
} else {
console.error('Unexpected error:', error);
}
}
```
## Next Steps
Learn how to create and execute tasks
Manage agent sessions
Add more capabilities to your agents
Explore advanced patterns
# Installation & Setup
Source: https://docs.julep.ai/sdks/nodejs/installation
Get started with the Julep Node.js SDK
## Installation
The Julep Node.js SDK can be installed using npm, yarn, or bun:
```bash theme={"dark"}
# Using npm
npm install @julep/sdk
# Using yarn
yarn add @julep/sdk
# Using bun
bun add @julep/sdk
```
## Configuration
After installation, you'll need to configure the SDK with your API key:
```javascript theme={"dark"}
const { Julep } = require('@julep/sdk');
// Or using ES modules
import { Julep } from '@julep/sdk';
const client = new Julep({
apiKey: 'your_api_key',
environment: 'production', // or 'development'
// Optional configuration
timeout: 30000, // Request timeout in milliseconds
retries: 3, // Number of retries for failed requests
baseUrl: 'https://api.julep.ai' // Custom API endpoint if needed
});
```
## Environment Variables
We recommend using environment variables to manage your API key securely:
```javascript theme={"dark"}
// Load environment variables
require('dotenv').config();
const client = new Julep({
apiKey: process.env.JULEP_API_KEY,
environment: process.env.JULEP_ENVIRONMENT || 'production'
});
```
Example `.env` file:
```plaintext theme={"dark"}
JULEP_API_KEY=your_api_key_here
JULEP_ENVIRONMENT=production
```
## TypeScript Support
The SDK is written in TypeScript and includes type definitions out of the box. No additional installation is required for TypeScript support.
```typescript theme={"dark"}
import { Julep, Agent, Task, Execution } from '@julep/sdk';
const client = new Julep({
apiKey: process.env.JULEP_API_KEY
});
async function createAgent(): Promise {
return await client.agents.create({
name: 'My Agent',
model: 'claude-3.5-sonnet',
about: 'A helpful AI assistant'
});
}
```
## Verification
To verify your installation and configuration, you can run a simple test:
```javascript theme={"dark"}
async function testConnection() {
try {
const agent = await client.agents.create({
name: 'Test Agent',
model: 'claude-3.5-sonnet',
about: 'Testing the SDK setup'
});
console.log('Successfully connected to Julep!', agent);
} catch (error) {
console.error('Connection test failed:', error);
}
}
testConnection();
```
## Next Steps
Learn how to create and manage AI agents
Create and execute tasks with your agents
Manage conversational sessions
Add capabilities to your agents
# Node.js SDK Reference
Source: https://docs.julep.ai/sdks/nodejs/reference
Complete reference documentation for the Node.js SDK
# Shared
Types:
* ResourceCreated
* ResourceDeleted
* ResourceUpdated
# Agents
Types:
* Agent
Methods:
* client.agents.create(\{ ...params }) -> ResourceCreated
* client.agents.update(agentId, \{ ...params }) -> ResourceUpdated
* client.agents.list(\{ ...params }) -> AgentsOffsetPagination
* client.agents.delete(agentId) -> ResourceDeleted
* client.agents.createOrUpdate(agentId, \{ ...params }) -> ResourceCreated
* client.agents.get(agentId) -> Agent
* client.agents.reset(agentId, \{ ...params }) -> ResourceUpdated
## Tools
Types:
* ToolListResponse
Methods:
* client.agents.tools.create(agentId, \{ ...params }) -> ResourceCreated
* client.agents.tools.update(agentId, toolId, \{ ...params }) -> ResourceUpdated
* client.agents.tools.list(agentId, \{ ...params }) -> ToolListResponsesOffsetPagination
* client.agents.tools.delete(agentId, toolId) -> ResourceDeleted
* client.agents.tools.reset(agentId, toolId, \{ ...params }) -> ResourceUpdated
## Docs
Types:
* DocSearchResponse
Methods:
* client.agents.docs.create(agentId, \{ ...params }) -> ResourceCreated
* client.agents.docs.list(agentId, \{ ...params }) -> DocsOffsetPagination
* client.agents.docs.delete(agentId, docId) -> ResourceDeleted
* client.agents.docs.search(agentId, \{ ...params }) -> DocSearchResponse
# Files
Types:
* File
Methods:
* client.files.create(\{ ...params }) -> ResourceCreated
* client.files.delete(fileId) -> ResourceDeleted
* client.files.get(fileId) -> File
# Sessions
Types:
* ChatInput
* ChatResponse
* ChatSettings
* Entry
* History
* Message
* Session
* SessionChatResponse
Methods:
* client.sessions.create(\{ ...params }) -> ResourceCreated
* client.sessions.update(sessionId, \{ ...params }) -> ResourceUpdated
* client.sessions.list(\{ ...params }) -> SessionsOffsetPagination
* client.sessions.delete(sessionId) -> ResourceDeleted
* client.sessions.chat(sessionId, \{ ...params }) -> SessionChatResponse
* client.sessions.createOrUpdate(sessionId, \{ ...params }) -> ResourceUpdated
* client.sessions.get(sessionId) -> Session
* client.sessions.history(sessionId) -> History
* client.sessions.reset(sessionId, \{ ...params }) -> ResourceUpdated
# Users
Types:
* User
Methods:
* client.users.create(\{ ...params }) -> ResourceCreated
* client.users.update(userId, \{ ...params }) -> ResourceUpdated
* client.users.list(\{ ...params }) -> UsersOffsetPagination
* client.users.delete(userId) -> ResourceDeleted
* client.users.createOrUpdate(userId, \{ ...params }) -> ResourceCreated
* client.users.get(userId) -> User
* client.users.reset(userId, \{ ...params }) -> ResourceUpdated
## Docs
Types:
* DocSearchResponse
Methods:
* client.users.docs.create(userId, \{ ...params }) -> ResourceCreated
* client.users.docs.list(userId, \{ ...params }) -> DocsOffsetPagination
* client.users.docs.delete(userId, docId) -> ResourceDeleted
* client.users.docs.search(userId, \{ ...params }) -> DocSearchResponse
# Jobs
Types:
* JobStatus
Methods:
* client.jobs.get(jobId) -> JobStatus
# Docs
Types:
* Doc
* EmbedQueryResponse
* Snippet
Methods:
* client.docs.embed(\{ ...params }) -> EmbedQueryResponse
* client.docs.get(docId) -> Doc
# Tasks
Types:
* Task
Methods:
* client.tasks.create(agentId, \{ ...params }) -> ResourceCreated
* client.tasks.list(agentId, \{ ...params }) -> TasksOffsetPagination
* client.tasks.createOrUpdate(agentId, taskId, \{ ...params }) -> ResourceUpdated
* client.tasks.get(taskId) -> Task
# Executions
Types:
* Execution
* Transition
* ExecutionChangeStatusResponse
Methods:
* client.executions.create(taskId, \{ ...params }) -> ResourceCreated
* client.executions.list(taskId, \{ ...params }) -> ExecutionsOffsetPagination
* client.executions.changeStatus(executionId, \{ ...params }) -> unknown
* client.executions.get(executionId) -> Execution
## Transitions
Types:
* TransitionStreamResponse
Methods:
* client.executions.transitions.list(executionId, \{ ...params }) -> TransitionsOffsetPagination
* client.executions.transitions.stream(executionId, \{ ...params }) -> unknown \*/} \*/}
# Managing Secrets with Node.js SDK
Source: https://docs.julep.ai/sdks/nodejs/secrets
How to manage secrets using the Julep Node.js SDK
# Managing Secrets with Node.js SDK
This guide covers how to manage secrets using the Julep Node.js SDK. Secrets allow you to securely store and use sensitive information like API keys, passwords, and access tokens in your Julep applications.
## Installation
Ensure you have the latest version of the Node.js SDK:
```bash theme={"dark"}
npm install @julep/sdk
# or
yarn add @julep/sdk
```
## Authentication
Initialize the client with your API key:
```javascript theme={"dark"}
import { Julep } from '@julep/sdk';
const julep = new Julep({ apiKey: 'your_api_key' });
```
## Creating Secrets
Create a new secret:
```javascript theme={"dark"}
const secret = await julep.secrets.create({
name: 'stripe_api_key',
value: 'sk_test_...',
description: 'Stripe API key for payment processing',
metadata: { environment: 'production', owner: 'payments-team' }
});
console.log(`Created secret: ${secret.name}`);
```
### Required Parameters
* `name`: The name of the secret (must be a valid identifier)
* `value`: The secret value to encrypt and store
### Optional Parameters
* `description`: A description of what the secret is used for
* `metadata`: An object of metadata to associate with the secret
## Listing Secrets
List all available secrets:
```javascript theme={"dark"}
// List all secrets
const secrets = await julep.secrets.list();
secrets.items.forEach(secret => {
console.log(`${secret.name}: ${secret.description}`);
});
```
### Pagination
Use pagination to handle large numbers of secrets:
```javascript theme={"dark"}
// List with pagination
const secretsPage1 = await julep.secrets.list({ limit: 10, offset: 0 });
const secretsPage2 = await julep.secrets.list({ limit: 10, offset: 10 });
```
### Filtering by Metadata
Filter secrets based on metadata:
```javascript theme={"dark"}
// Filter by metadata
const productionSecrets = await julep.secrets.list({
metadata: { environment: 'production' }
});
// Filter by multiple metadata fields
const teamSecrets = await julep.secrets.list({
metadata: {
environment: 'production',
owner: 'payments-team'
}
});
```
## Retrieving Secrets
Get a specific secret by name:
```javascript theme={"dark"}
const secret = await julep.secrets.get({ name: 'stripe_api_key' });
console.log(`Secret: ${secret.name}, Created: ${secret.createdAt}`);
// Access the secret value
console.log(`Secret value: ${secret.value}`);
```
> **Note**: For security, when listing secrets, the `.value` field will always show "ENCRYPTED". The actual secret value is only returned when specifically requesting a single secret by name.
## Updating Secrets
Update an existing secret:
```javascript theme={"dark"}
const updatedSecret = await julep.secrets.update({
name: 'stripe_api_key',
value: 'sk_test_new_value...',
description: 'Updated Stripe API key',
metadata: {
environment: 'production',
owner: 'payments-team',
rotated: '2025-05-10'
}
});
console.log(`Updated secret: ${updatedSecret.name}`);
```
### Partial Updates
You can update specific fields without changing others:
```javascript theme={"dark"}
// Update only the description
const updatedSecret = await julep.secrets.update({
name: 'stripe_api_key',
description: 'New description for Stripe API key'
});
// Update only the metadata
const updatedMetadata = await julep.secrets.update({
name: 'stripe_api_key',
metadata: { lastRotated: '2025-05-10' }
});
```
## Deleting Secrets
Delete a secret when it's no longer needed:
```javascript theme={"dark"}
await julep.secrets.delete({ name: 'stripe_api_key' });
console.log('Secret deleted');
```
## Using Secrets in Tasks
Reference secrets when executing tasks:
```javascript [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
const julep = new Julep({ apiKey: 'your_api_key' });
// Define a task that uses a secret
const taskDefinition = {
steps: [
{
kind: 'tool_call',
tool: 'openai',
operation: 'chat',
arguments: {
model: 'gpt-4',
messages: [
{ role: 'user', content: 'What\'s the weather like?' }
]
},
secret_name: 'openai_api_key'
}
]
};
// Create and execute the task
const task = await julep.tasks.create({ task: taskDefinition });
const execution = await julep.tasks.execute({ taskId: task.id });
```
### Using Secrets in Expressions
You can reference secrets in expressions:
```javascript theme={"dark"}
const taskDefinition = {
steps: [
{
kind: 'transform',
expression: "$ f'https://api.example.com/v1?api_key={secrets.api_key}&query={input}'",
input: 'search query',
output: 'api_url'
}
]
};
```
### Using Multiple Secrets
For tools that require multiple secrets:
```javascript theme={"dark"}
const taskDefinition = {
steps: [
{
kind: 'tool_call',
tool: 'database',
operation: 'query',
arguments: {
query: 'SELECT * FROM users',
connection: {
host: '$ secrets.db_host',
user: '$ secrets.db_username',
password: '$ secrets.db_password',
database: '$ secrets.db_name'
}
}
}
]
};
```
## Error Handling
Handle common errors when working with secrets:
```javascript [expandable] theme={"dark"}
try {
const secret = await julep.secrets.get({ name: 'non_existent_secret' });
} catch (error) {
if (error.code === 'not_found') {
console.log('Secret not found');
} else {
console.error('Error retrieving secret:', error);
}
}
try {
// Attempt to create a secret with an invalid name
const secret = await julep.secrets.create({ name: 'invalid name', value: 'test' });
} catch (error) {
if (error.code === 'validation_error') {
console.log(`Validation error: ${error.message}`);
} else {
console.error('Error creating secret:', error);
}
}
try {
// Attempt to create a duplicate secret
const secret = await julep.secrets.create({ name: 'existing_secret', value: 'test' });
} catch (error) {
if (error.code === 'conflict') {
console.log('Secret already exists');
} else {
console.error('Error creating secret:', error);
}
}
```
## Secret Rotation Example
Implement a secret rotation policy:
```javascript [expandable] theme={"dark"}
import { v4 as uuidv4 } from 'uuid';
/**
* Safely rotate a secret by creating a new one and verifying it works
* before deleting the old one.
*/
async function rotateSecret(julep, secretName, newValue) {
// Create a temporary secret with a random suffix
const tempName = `${secretName}_rotation_${uuidv4().substring(0, 8)}`;
// Create the new secret
await julep.secrets.create({
name: tempName,
value: newValue,
description: `Temporary rotation for ${secretName}`,
metadata: { rotationDate: new Date().toISOString() }
});
// Here you would test that the new secret works
// ...
// If tests pass, update metadata on the old secret
const oldSecret = await julep.secrets.get({ name: secretName });
const oldMetadata = oldSecret.metadata || {};
await julep.secrets.update({
name: secretName,
metadata: {
...oldMetadata,
archived: 'true',
replacedBy: tempName,
archivedDate: new Date().toISOString()
}
});
// Rename the temporary secret to the standard name
await julep.secrets.delete({ name: secretName });
await julep.secrets.update({
name: tempName,
newName: secretName,
description: oldSecret.description,
metadata: { lastRotated: new Date().toISOString() }
});
return await julep.secrets.get({ name: secretName });
}
// Example usage
async function rotateStripeKey() {
const newKey = 'sk_test_new_value_after_rotation';
try {
const rotatedSecret = await rotateSecret(julep, 'stripe_api_key', newKey);
console.log(`Rotated secret: ${rotatedSecret.name}`);
} catch (error) {
console.error('Error rotating secret:', error);
}
}
```
## Working with Async/Await
All methods in the Node.js SDK return Promises, making them compatible with async/await:
```javascript [expandable] theme={"dark"}
async function manageSecrets() {
try {
// Create a secret
const secret = await julep.secrets.create({
name: 'database_password',
value: 'complex-password-123',
description: 'Production database password'
});
// List all secrets
const secrets = await julep.secrets.list();
// Create task using the secret
const task = await julep.tasks.create({
steps: [
{
kind: 'tool_call',
tool: 'database',
operation: 'query',
secret_name: 'database_password'
}
]
});
return { secret, secrets, task };
} catch (error) {
console.error('Error managing secrets:', error);
throw error;
}
}
```
## Best Practices
1. Use a consistent naming convention for all secrets
2. Add detailed descriptions and metadata to make secrets discoverable
3. Implement a rotation policy for sensitive secrets
4. Log secret operations (creation, updates, deletions) but never log values
5. Use try/catch blocks to handle potential errors gracefully
6. Consider automating secret rotation for important credentials
7. Use metadata to track important dates and ownership information
## Next Steps
* [Common Secrets Patterns](/sdks/common/secrets) - Patterns that work across all SDKs
* [Secrets Management](/advanced/secrets-management) - Advanced guide for managing secrets
* [Using Secrets in Julep](/guides/using-secrets) - Step-by-step guide for using secrets
# Sessions
Source: https://docs.julep.ai/sdks/nodejs/sessions
Manage conversational sessions with the Node.js SDK
Sessions in Julep enable persistent, stateful interactions between users and agents. They maintain context across multiple exchanges and can be used to build conversational interfaces.
## Creating Sessions
```javascript theme={"dark"}
// Create a new session
const session = await client.sessions.create({
agent_id: agentId,
user_id: userId, // Optional
metadata: {
channel: 'web',
language: 'english'
},
context_overflow: 'adaptive' // or 'truncate' or 'summarize'
});
```
## Session Chat
```javascript theme={"dark"}
// Send a message and get a response
const response = await client.sessions.chat(sessionId, {
messages: [
{
role: 'user',
content: 'Hello! Can you help me with my order?'
}
]
});
// Continue the conversation in the same session
const followUp = await client.sessions.chat(sessionId, {
messages: [
{
role: 'user',
content: 'I need to change my shipping address.'
}
]
});
```
## Managing Context
Sessions automatically maintain context, but you can also manage it manually:
```javascript theme={"dark"}
// Add context to the session
await client.sessions.update(sessionId, {
metadata: {
order_id: '12345',
customer_tier: 'premium'
}
});
// Get session history
const history = await client.sessions.history(sessionId, {
limit: 10,
offset: 0
});
```
## Session Tools
Tools can be added specifically for a session:
```javascript theme={"dark"}
// Add a session-specific tool
await client.sessions.tools.create(sessionId, {
name: 'check_order_status',
description: 'Check the status of an order',
type: 'function',
function: {
parameters: {
type: 'object',
properties: {
order_id: {
type: 'string',
description: 'The order ID to check'
}
},
required: ['order_id']
}
}
});
```
## Session Documents
Add relevant documents to the session for context:
```javascript theme={"dark"}
// Add a document to the session
const document = await client.sessions.docs.create(sessionId, {
title: 'Order Details',
content: 'Order #12345: 2 items, shipping to...',
metadata: {
order_id: '12345',
type: 'order_details'
}
});
// Search session documents
const docs = await client.sessions.docs.search(sessionId, {
query: 'shipping policy',
metadata: {
type: 'policy'
}
});
```
## Managing Sessions
```javascript theme={"dark"}
// Get a specific session
const session = await client.sessions.get(sessionId);
// List all sessions
const sessions = await client.sessions.list({
limit: 10,
offset: 0,
agent_id: agentId // Optional filter
});
// Update a session
const updatedSession = await client.sessions.update(sessionId, {
metadata: {
status: 'resolved'
}
});
// Delete a session
await client.sessions.delete(sessionId);
```
## Error Handling
```javascript theme={"dark"}
try {
const response = await client.sessions.chat(sessionId, {
messages: [
{
role: 'user',
content: 'Hello!'
}
]
});
} catch (error) {
if (error.name === 'SessionError') {
console.error('Session error:', error.message);
} else if (error.name === 'ApiError') {
console.error('API error:', error.message, error.status);
} else {
console.error('Unexpected error:', error);
}
}
```
## Context Overflow Strategies
Julep provides different strategies for handling context overflow:
```javascript theme={"dark"}
// Adaptive strategy (default)
const adaptiveSession = await client.sessions.create({
agent_id: agentId,
context_overflow: 'adaptive'
});
// Truncate strategy
const truncateSession = await client.sessions.create({
agent_id: agentId,
context_overflow: 'truncate'
});
// Summarize strategy
const summarizeSession = await client.sessions.create({
agent_id: agentId,
context_overflow: 'summarize'
});
```
## Next Steps
Add tools to your sessions
Explore advanced patterns
View the complete API reference
See real-world examples
# Tasks
Source: https://docs.julep.ai/sdks/nodejs/tasks
Create and manage tasks with the Node.js SDK
Tasks are multi-step workflows that agents can execute. They can include prompts, tool calls, conditional logic, and more.
## Creating Tasks
Tasks can be created using either YAML or JavaScript objects:
```javascript [expandable] theme={"dark"}
// Using a JavaScript object
const task = await client.tasks.create(agentId, {
name: 'Customer Support Task',
description: 'Handle customer support requests',
main: [
{
prompt: [
{
role: 'system',
content: 'You are a helpful customer support agent.'
},
{
role: 'user',
content: '{{_.user_query}}'
}
]
},
{
tool: 'web_search',
arguments: {
query: '{{_.user_query}}'
}
}
]
});
// Using YAML
const taskYaml = `
name: Customer Support Task
description: Handle customer support requests
main:
- prompt:
- role: system
content: You are a helpful customer support agent.
- role: user
content: "{{_.user_query}}"
- tool: web_search
arguments:
query: "{{_.user_query}}"
`;
const task = await client.tasks.create(agentId, yaml.parse(taskYaml));
```
## Task Steps
Tasks can include various types of steps:
```javascript [expandable] theme={"dark"}
const task = await client.tasks.create(agentId, {
name: 'Complex Task',
description: 'A task with multiple step types',
main: [
// Prompt step
{
prompt: 'Analyze the following data: {{_.data}}'
},
// Tool call step
{
tool: 'web_search',
arguments: {
query: '{{_.search_query}}'
}
},
// Evaluate step
{
evaluate: {
average_score: 'sum(_.scores) / len(_.scores)'
}
},
// Conditional step
{
if: '_.score > 0.8',
then: [
{ log: 'High score achieved' }
],
else: [
{ error: 'Score too low' }
]
},
// Iteration step
{
foreach: {
in: '_.items',
do: [
{ log: 'Processing item {{_}}' }
]
}
},
// Parallel execution
{
parallel: [
{
tool: 'web_search',
arguments: { query: 'query1' }
},
{
tool: 'web_search',
arguments: { query: 'query2' }
}
]
}
]
});
```
## Executing Tasks
```javascript theme={"dark"}
// Execute a task
const execution = await client.executions.create(taskId, {
input: {
user_query: 'How do I reset my password?'
}
});
// Get execution status
const status = await client.executions.get(execution.id);
// Wait for execution to complete
while (status.status !== 'succeeded' && status.status !== 'failed') {
await new Promise(resolve => setTimeout(resolve, 1000));
const updatedStatus = await client.executions.get(execution.id);
console.log('Execution status:', updatedStatus.status);
}
```
## Managing Tasks
```javascript theme={"dark"}
// Get a specific task
const task = await client.tasks.get(taskId);
// List all tasks
const tasks = await client.tasks.list({
limit: 10,
offset: 0
});
// Update a task
const updatedTask = await client.tasks.update(taskId, {
description: 'Updated task description'
});
// Delete a task
await client.tasks.delete(taskId);
```
## Error Handling
```javascript theme={"dark"}
try {
const execution = await client.executions.create(taskId, {
input: {
user_query: 'How do I reset my password?'
}
});
} catch (error) {
if (error.name === 'ValidationError') {
console.error('Invalid task configuration:', error.message);
} else if (error.name === 'ExecutionError') {
console.error('Execution failed:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
```
## Next Steps
Learn about session management
Add tools to your tasks
Explore advanced patterns
View the complete API reference
# Tools Integration
Source: https://docs.julep.ai/sdks/nodejs/tools-integration
Add powerful capabilities to your agents with tools
Tools in Julep extend your agents' capabilities by allowing them to interact with external services and perform specific functions. There are several types of tools available:
## Tool Types
1. **User-defined Functions**: Custom functions that you implement
2. **System Tools**: Built-in tools for interacting with Julep's APIs
3. **Integrations**: Pre-built integrations with third-party services
4. **Direct API Calls**: Make HTTP requests to external APIs
## User-defined Functions
Create custom tools that your agents can use:
```javascript [expandable] theme={"dark"}
// Create a custom function tool
const tool = await client.agents.tools.create(agentId, {
name: 'calculate_discount',
description: 'Calculate the final price after applying a discount',
type: 'function',
function: {
parameters: {
type: 'object',
properties: {
original_price: {
type: 'number',
description: 'Original price before discount'
},
discount_percentage: {
type: 'number',
description: 'Discount percentage (0-100)'
}
},
required: ['original_price', 'discount_percentage']
}
}
});
```
## System Tools
Use built-in tools to interact with Julep's APIs:
```javascript theme={"dark"}
// Add a system tool for listing agents
const systemTool = await client.agents.tools.create(agentId, {
name: 'list_agent_docs',
description: 'List all documents for the given agent',
type: 'system',
system: {
resource: 'agent',
subresource: 'doc',
operation: 'list'
}
});
```
## Built-in Integrations
Julep provides several pre-built integrations:
### Brave Search Integration
```javascript theme={"dark"}
// Add Brave Search integration
const braveSearch = await client.agents.tools.create(agentId, {
name: 'web_search',
description: 'Search the web for information',
integration: {
provider: 'brave',
method: 'search',
setup: {
brave_api_key: process.env.BRAVE_API_KEY
}
}
});
```
### Email Integration
```javascript theme={"dark"}
// Add email integration
const emailTool = await client.agents.tools.create(agentId, {
name: 'send_email',
description: 'Send an email',
integration: {
provider: 'email',
setup: {
host: 'smtp.example.com',
port: 587,
user: process.env.EMAIL_USER,
password: process.env.EMAIL_PASSWORD
}
}
});
```
### Weather Integration
```javascript theme={"dark"}
// Add weather integration
const weatherTool = await client.agents.tools.create(agentId, {
name: 'check_weather',
description: 'Get weather information',
integration: {
provider: 'weather',
setup: {
openweathermap_api_key: process.env.OPENWEATHER_API_KEY
}
}
});
```
### Wikipedia Integration
```javascript theme={"dark"}
// Add Wikipedia integration
const wikiTool = await client.agents.tools.create(agentId, {
name: 'wiki_search',
description: 'Search Wikipedia articles',
integration: {
provider: 'wikipedia'
}
});
```
## Direct API Calls
Make direct HTTP requests to external APIs:
```javascript theme={"dark"}
// Add a direct API call tool with params_schema
const apiTool = await client.agents.tools.create(agentId, {
name: 'github_stars',
description: 'Get GitHub repository stars',
type: 'api_call',
api_call: {
method: 'GET',
url: 'https://api.github.com/repos/{{owner}}/{{repo}}',
headers: {
Authorization: 'Bearer {{github_token}}'
},
params_schema: {
type: 'object',
properties: {
owner: {
type: 'string',
description: 'Repository owner (username or organization)'
},
repo: {
type: 'string',
description: 'Repository name'
}
},
required: ['owner', 'repo']
}
}
});
```
## Using Tools in Tasks
Once tools are added to an agent, they can be used in tasks:
```javascript [expandable] theme={"dark"}
const task = await client.tasks.create(agentId, {
name: 'Research Task',
description: 'Research a topic using multiple tools',
main: [
// Use web search
{
tool: 'web_search',
arguments: {
query: '{{_.topic}}'
}
},
// Use Wikipedia
{
tool: 'wiki_search',
arguments: {
query: '{{_.topic}}'
}
},
// Send results via email
{
tool: 'send_email',
arguments: {
to: '{{_.email}}',
subject: 'Research Results: {{_.topic}}',
body: '{{_.results}}'
}
}
]
});
```
## Tool Management
```javascript theme={"dark"}
// List tools for an agent
const tools = await client.agents.tools.list(agentId);
// Get a specific tool
const tool = await client.agents.tools.get(agentId, toolId);
// Update a tool
const updatedTool = await client.agents.tools.update(agentId, toolId, {
description: 'Updated tool description'
});
// Delete a tool
await client.agents.tools.delete(agentId, toolId);
```
## Error Handling
```javascript theme={"dark"}
try {
const tool = await client.agents.tools.create(agentId, {
name: 'web_search',
integration: {
provider: 'brave',
setup: {
brave_api_key: process.env.BRAVE_API_KEY
}
}
});
} catch (error) {
if (error.name === 'ValidationError') {
console.error('Invalid tool configuration:', error.message);
} else if (error.name === 'IntegrationError') {
console.error('Integration setup failed:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
```
## Next Steps
Learn advanced patterns and best practices
View the complete API reference
See real-world examples
Learn common integration patterns
# Advanced Usage
Source: https://docs.julep.ai/sdks/python/advanced-usage
Advanced patterns and best practices for the Python SDK
## Async Operations
Use the async client for better performance:
```python theme={"dark"}
from julep import AsyncJulep
import asyncio
async def main():
client = AsyncJulep(api_key="your_julep_api_key")
# Create multiple agents concurrently
agents = await asyncio.gather(*[
client.agents.create(name=f"Agent {i}")
for i in range(5)
])
# Execute multiple tasks concurrently
executions = await asyncio.gather(*[
client.executions.create(task_id=task.id)
for task in tasks
])
if __name__ == "__main__":
asyncio.run(main())
```
## Complex Workflows
Create sophisticated task workflows:
```yaml [expandable] theme={"dark"}
name: Advanced Workflow
description: Complex task with multiple steps and error handling
tools:
- name: web_search
type: integration
integration:
provider: brave
method: search
- name: process_data
type: function
function:
parameters:
type: object
properties:
data:
type: array
items:
type: string
main:
# Parallel processing with error handling
- try:
- map_reduce:
over: _.topics
map:
- tool: web_search
arguments:
query: _
parallelism: 5
catch:
- log: Search failed
- return: {"error": "Search operation failed"}
# Conditional branching
- if: len(_.search_results) > 0
then:
- evaluate:
processed_data: process_results(_.search_results)
else:
- return: {"error": "No results found"}
# Custom aggregation
- evaluate:
summary: aggregate_results(_.processed_data)
confidence: calculate_confidence(_.processed_data)
# Dynamic tool selection
- switch:
- case: _.confidence > 0.8
then:
- tool: high_confidence_processor
- case: _.confidence > 0.5
then:
- tool: medium_confidence_processor
- case: _
then:
- tool: low_confidence_processor
```
## Custom Tool Implementation
Work in progress. We're working on a way to allow you to implement your own tools.
## Advanced Error Handling
Work in progress. We're working on a way to handle errors in a more robust way.
## Performance Optimization
Work in progress. We're working on a way to optimize the performance of the SDK.
## Testing Strategies
Work in progress. We're working on a way to test the SDK.
# Working with Agents
Source: https://docs.julep.ai/sdks/python/agents
Learn how to create and manage AI agents using the Python SDK
## Creating an Agent
Create an AI agent with specific capabilities:
```python theme={"dark"}
agent = client.agents.create(
name="Research Assistant",
model="claude-3.5-sonnet", # or any supported model
about="A helpful research assistant that can search and summarize information.",
metadata={
"expertise": "research",
"language": "english"
}
)
```
## Retrieving Agents
```python theme={"dark"}
# Get a specific agent
agent = client.agents.get(agent_id="agent_123")
# List all agents
agents = client.agents.list(
limit=10,
offset=0,
metadata_filter={"expertise": "research"}
)
```
## Updating Agents
```python theme={"dark"}
updated_agent = client.agents.update(
agent_id="agent_123",
name="Advanced Research Assistant",
metadata={"expertise": ["research", "analysis"]}
)
```
## Managing Agent Tools
Add capabilities to your agent by attaching tools:
```python theme={"dark"}
# Add a web search tool
client.agents.tools.create(
agent_id=agent.id,
name="web_search",
description="Search the web for information",
integration={
"provider": "brave",
"method": "search",
"setup": {"brave_api_key": "your_brave_api_key"}
}
)
# List agent's tools
tools = client.agents.tools.list(agent_id=agent.id)
# Remove a tool
client.agents.tools.delete(
agent_id=agent.id,
tool_id="tool_123"
)
```
## Working with Documents
Manage documents associated with your agent:
```python [expandable] theme={"dark"}
# Add a document
doc = client.agents.docs.create(
agent_id=agent.id,
title="Research Paper",
content="Content of the research paper...",
metadata={"category": "science"}
)
# Search documents
results = client.agents.docs.search(
agent_id=agent.id,
text="quantum physics",
metadata_filter={"category": "science"},
limit=5
)
# Delete a document
client.agents.docs.delete(
agent_id=agent.id,
doc_id="doc_123"
)
```
## Deleting Agents
```python theme={"dark"}
client.agents.delete(agent_id="agent_123")
```
## Error Handling
```python theme={"dark"}
from julep.exceptions import JulepError, AgentNotFoundError
try:
agent = client.agents.get("nonexistent_id")
except AgentNotFoundError:
print("Agent not found")
except JulepError as e:
print(f"An error occurred: {e}")
```
# Installation & Setup
Source: https://docs.julep.ai/sdks/python/installation
Getting started with the Python SDK
## Installation
Install the Julep Python SDK using pip:
```bash theme={"dark"}
pip install julep
```
## Quick Setup
```python theme={"dark"}
from julep import Julep
# Initialize the client
client = Julep(api_key="your_julep_api_key")
```
## Environment Setup
You can also configure the client using environment variables:
```bash theme={"dark"}
export JULEP_API_KEY=your_julep_api_key
export JULEP_ENVIRONMENT=production # or development
```
Then initialize without parameters:
```python theme={"dark"}
from julep import Julep
client = Julep() # Will use environment variables
```
## Configuration Options
The Julep client can be configured with several options:
```python theme={"dark"}
client = Julep(
api_key="your_julep_api_key",
environment="production", # or "development"
base_url="https://api.julep.ai", # Optional: custom API endpoint
timeout=30 # Optional: custom timeout in seconds
)
```
## Async Support
Julep also provides an async client for use with asyncio:
```python theme={"dark"}
from julep import AsyncJulep
async def main():
client = AsyncJulep(api_key="your_julep_api_key")
# Use the client asynchronously
agent = await client.agents.create(name="My Agent")
# Run with asyncio
import asyncio
asyncio.run(main())
```
# Python SDK Reference
Source: https://docs.julep.ai/sdks/python/reference
Complete reference documentation for the Python SDK
# Shared Types
```python theme={"dark"}
from julep.types import ResourceCreated, ResourceDeleted, ResourceUpdated
```
# Agents
Types:
```python theme={"dark"}
from julep.types import Agent
```
Methods:
* client.agents.create(\*\*params) -> ResourceCreated
* client.agents.update(agent\_id, \*\*params) -> ResourceUpdated
* client.agents.list(\*\*params) -> SyncOffsetPagination\[Agent]
* client.agents.delete(agent\_id) -> ResourceDeleted
* client.agents.create\_or\_update(agent\_id, \*\*params) -> ResourceCreated
* client.agents.get(agent\_id) -> Agent
* client.agents.reset(agent\_id, \*\*params) -> ResourceUpdated
## Tools
Types:
```python theme={"dark"}
from julep.types.agents import ToolListResponse
```
Methods:
* client.agents.tools.create(agent\_id, \*\*params) -> ResourceCreated
* client.agents.tools.update(tool\_id, \*, agent\_id, \*\*params) -> ResourceUpdated
* client.agents.tools.list(agent\_id, \*\*params) -> SyncOffsetPagination\[ToolListResponse]
* client.agents.tools.delete(tool\_id, \*, agent\_id) -> ResourceDeleted
* client.agents.tools.reset(tool\_id, \*, agent\_id, \*\*params) -> ResourceUpdated
## Docs
Types:
```python theme={"dark"}
from julep.types.agents import DocSearchResponse
```
Methods:
* client.agents.docs.create(agent\_id, \*\*params) -> ResourceCreated
* client.agents.docs.list(agent\_id, \*\*params) -> SyncOffsetPagination\[Doc]
* client.agents.docs.delete(doc\_id, \*, agent\_id) -> ResourceDeleted
* client.agents.docs.search(agent\_id, \*\*params) -> DocSearchResponse
# Files
Types:
```python theme={"dark"}
from julep.types import File
```
Methods:
* client.files.create(\*\*params) -> ResourceCreated
* client.files.delete(file\_id) -> ResourceDeleted
* client.files.get(file\_id) -> File
# Sessions
Types:
```python theme={"dark"}
from julep.types import (
ChatInput,
ChatResponse,
ChatSettings,
Entry,
History,
Message,
Session,
SessionChatResponse,
)
```
Methods:
* client.sessions.create(\*\*params) -> ResourceCreated
* client.sessions.update(session\_id, \*\*params) -> ResourceUpdated
* client.sessions.list(\*\*params) -> SyncOffsetPagination\[Session]
* client.sessions.delete(session\_id) -> ResourceDeleted
* client.sessions.chat(session\_id, \*\*params) -> SessionChatResponse
* client.sessions.create\_or\_update(session\_id, \*\*params) -> ResourceUpdated
* client.sessions.get(session\_id) -> Session
* client.sessions.history(session\_id) -> History
* client.sessions.reset(session\_id, \*\*params) -> ResourceUpdated
# Users
Types:
```python theme={"dark"}
from julep.types import User
```
Methods:
* client.users.create(\*\*params) -> ResourceCreated
* client.users.update(user\_id, \*\*params) -> ResourceUpdated
* client.users.list(\*\*params) -> SyncOffsetPagination\[User]
* client.users.delete(user\_id) -> ResourceDeleted
* client.users.create\_or\_update(user\_id, \*\*params) -> ResourceCreated
* client.users.get(user\_id) -> User
* client.users.reset(user\_id, \*\*params) -> ResourceUpdated
## Docs
Types:
```python theme={"dark"}
from julep.types.users import DocSearchResponse
```
Methods:
* client.users.docs.create(user\_id, \*\*params) -> ResourceCreated
* client.users.docs.list(user\_id, \*\*params) -> SyncOffsetPagination\[Doc]
* client.users.docs.delete(doc\_id, \*, user\_id) -> ResourceDeleted
* client.users.docs.search(user\_id, \*\*params) -> DocSearchResponse
# Jobs
Types:
```python theme={"dark"}
from julep.types import JobStatus
```
Methods:
* client.jobs.get(job\_id) -> JobStatus
# Docs
Types:
```python theme={"dark"}
from julep.types import Doc, EmbedQueryResponse, Snippet
```
Methods:
* client.docs.embed(\*\*params) -> EmbedQueryResponse
* client.docs.get(doc\_id) -> Doc
# Tasks
Types:
```python theme={"dark"}
from julep.types import Task
```
Methods:
* client.tasks.create(agent\_id, \*\*params) -> ResourceCreated
* client.tasks.list(agent\_id, \*\*params) -> SyncOffsetPagination\[Task]
* client.tasks.create\_or\_update(task\_id, \*, agent\_id, \*\*params) -> ResourceUpdated
* client.tasks.get(task\_id) -> Task
# Executions
Types:
```python theme={"dark"}
from julep.types import Execution, Transition, ExecutionChangeStatusResponse
```
Methods:
* client.executions.create(task\_id, \*\*params) -> ResourceCreated
* client.executions.list(task\_id, \*\*params) -> SyncOffsetPagination\[Execution]
* client.executions.change\_status(execution\_id, \*\*params) -> object
* client.executions.get(execution\_id) -> Execution
## Transitions
Types:
```python theme={"dark"}
from julep.types.executions import TransitionStreamResponse
```
Methods:
* client.executions.transitions.list(execution\_id, \*\*params) -> SyncOffsetPagination\[Transition]
* client.executions.transitions.stream(execution\_id, \*\*params) -> object
# Managing Secrets with Python SDK
Source: https://docs.julep.ai/sdks/python/secrets
How to manage secrets using the Julep Python SDK
# Managing Secrets with Python SDK
This guide covers how to manage secrets using the Julep Python SDK. Secrets allow you to securely store and use sensitive information like API keys, passwords, and access tokens in your Julep applications.
## Installation
Ensure you have the latest version of the Python SDK:
```bash theme={"dark"}
pip install julep
```
## Authentication
Initialize the client with your API key:
```python theme={"dark"}
from julep import Julep
client = Julep(api_key="your_api_key")
```
## Creating Secrets
Create a new secret:
```python theme={"dark"}
secret = client.secrets.create(
name="stripe_api_key",
value="sk_test_...",
description="Stripe API key for payment processing",
metadata={"environment": "production", "owner": "payments-team"}
)
print(f"Created secret: {secret.name}")
```
### Required Parameters
* `name`: The name of the secret (must be a valid identifier)
* `value`: The secret value to encrypt and store
### Optional Parameters
* `description`: A description of what the secret is used for
* `metadata`: A dictionary of metadata to associate with the secret
## Listing Secrets
List all available secrets:
```python theme={"dark"}
# List all secrets
secrets = client.secrets.list()
for secret in secrets.items:
print(f"{secret.name}: {secret.description}")
```
### Pagination
Use pagination to handle large numbers of secrets:
```python theme={"dark"}
# List with pagination
secrets_page_1 = client.secrets.list(limit=10, offset=0)
secrets_page_2 = client.secrets.list(limit=10, offset=10)
```
### Filtering by Metadata
Filter secrets based on metadata:
```python theme={"dark"}
# Filter by metadata
production_secrets = client.secrets.list(
metadata={"environment": "production"}
)
# Filter by multiple metadata fields
team_secrets = client.secrets.list(
metadata={
"environment": "production",
"owner": "payments-team"
}
)
```
## Retrieving Secrets
Get a specific secret by name:
```python theme={"dark"}
secret = client.secrets.get(name="stripe_api_key")
print(f"Secret: {secret.name}, Created: {secret.created_at}")
# Access the secret value
print(f"Secret value: {secret.value}")
```
> **Note**: For security, when listing secrets, the `.value` field will always show "ENCRYPTED". The actual secret value is only returned when specifically requesting a single secret by name.
## Updating Secrets
Update an existing secret:
```python theme={"dark"}
updated_secret = client.secrets.update(
name="stripe_api_key",
value="sk_test_new_value...",
description="Updated Stripe API key",
metadata={"environment": "production", "owner": "payments-team", "rotated": "2025-05-10"}
)
print(f"Updated secret: {updated_secret.name}")
```
### Partial Updates
You can update specific fields without changing others:
```python theme={"dark"}
# Update only the description
updated_secret = client.secrets.update(
name="stripe_api_key",
description="New description for Stripe API key"
)
# Update only the metadata
updated_secret = client.secrets.update(
name="stripe_api_key",
metadata={"last_rotated": "2025-05-10"}
)
```
## Deleting Secrets
Delete a secret when it's no longer needed:
```python theme={"dark"}
client.secrets.delete(name="stripe_api_key")
print("Secret deleted")
```
## Using Secrets in Tasks
Reference secrets when executing tasks:
```python [expandable] theme={"dark"}
from julep import Julep
client = Julep(api_key="your_api_key")
# Define a task that uses a secret
task_definition = {
"steps": [
{
"kind": "tool_call",
"tool": "openai",
"operation": "chat",
"arguments": {
"model": "gpt-4",
"messages": [
{"role": "user", "content": "What's the weather like?"}
]
},
"secret_name": "openai_api_key"
}
]
}
# Create and execute the task
task = client.tasks.create(task=task_definition)
execution = client.tasks.execute(task_id=task.id)
```
### Using Secrets in Expressions
You can reference secrets in expressions:
```python theme={"dark"}
task_definition = {
"steps": [
{
"kind": "transform",
"expression": "$ f'https://api.example.com/v1?api_key={secrets.api_key}&query={input}'",
"input": "search query",
"output": "api_url"
}
]
}
```
### Using Multiple Secrets
For tools that require multiple secrets:
```python theme={"dark"}
task_definition = {
"steps": [
{
"kind": "tool_call",
"tool": "database",
"operation": "query",
"arguments": {
"query": "SELECT * FROM users",
"connection": {
"host": "$ secrets.db_host",
"user": "$ secrets.db_username",
"password": "$ secrets.db_password",
"database": "$ secrets.db_name"
}
}
}
]
}
```
## Error Handling
Handle common errors when working with secrets:
```python [expandable] theme={"dark"}
from julep.exceptions import (
SecretNotFoundError,
SecretAlreadyExistsError,
ValidationError
)
try:
secret = client.secrets.get(name="non_existent_secret")
except SecretNotFoundError:
print("Secret not found")
try:
# Attempt to create a secret with an invalid name
secret = client.secrets.create(name="invalid name", value="test")
except ValidationError as e:
print(f"Validation error: {e}")
try:
# Attempt to create a duplicate secret
secret = client.secrets.create(name="existing_secret", value="test")
except SecretAlreadyExistsError:
print("Secret already exists")
```
## Secret Rotation Example
Implement a secret rotation policy:
```python [expandable] theme={"dark"}
import uuid
from datetime import datetime
def rotate_secret(client, secret_name, new_value):
"""
Safely rotate a secret by creating a new one and verifying it works
before deleting the old one.
"""
# Create a temporary secret with a random suffix
temp_name = f"{secret_name}_rotation_{uuid.uuid4().hex[:8]}"
# Create the new secret
client.secrets.create(
name=temp_name,
value=new_value,
description=f"Temporary rotation for {secret_name}",
metadata={"rotation_date": datetime.now().isoformat()}
)
# Here you would test that the new secret works
# ...
# If tests pass, update metadata on the old secret
old_secret = client.secrets.get(name=secret_name)
old_metadata = old_secret.metadata or {}
old_metadata.update({
"archived": "true",
"replaced_by": temp_name,
"archived_date": datetime.now().isoformat()
})
client.secrets.update(
name=secret_name,
metadata=old_metadata
)
# Rename the temporary secret to the standard name
client.secrets.delete(name=secret_name)
client.secrets.update(
name=temp_name,
new_name=secret_name,
description=old_secret.description,
metadata={"last_rotated": datetime.now().isoformat()}
)
return client.secrets.get(name=secret_name)
# Example usage
new_key = "sk_test_new_value_after_rotation"
rotated_secret = rotate_secret(client, "stripe_api_key", new_key)
print(f"Rotated secret: {rotated_secret.name}")
```
## Best Practices
1. Use a consistent naming convention for all secrets
2. Add detailed descriptions and metadata to make secrets discoverable
3. Implement a rotation policy for sensitive secrets
4. Log secret operations (creation, updates, deletions) but never log values
5. Use try/except blocks to handle potential errors gracefully
6. Consider automating secret rotation for important credentials
7. Use metadata to track important dates and ownership information
## Next Steps
* [Common Secrets Patterns](/sdks/common/secrets) - Patterns that work across all SDKs
* [Secrets Management](/advanced/secrets-management) - Advanced guide for managing secrets
* [Using Secrets in Julep](/guides/using-secrets) - Step-by-step guide for using secrets
# Working with Sessions
Source: https://docs.julep.ai/sdks/python/sessions
Learn how to manage sessions and maintain conversation context using the Python SDK
## Creating Sessions
Create a session to maintain conversation context:
```python theme={"dark"}
session = client.sessions.create(
agent_id=agent.id,
user_id=user.id, # Optional
context_overflow="adaptive", # or "truncate" or "summarize"
metadata={
"channel": "web",
"language": "english"
}
)
```
## Session Chat
Interact with an agent through a session:
```python [expandable] theme={"dark"}
# Single message
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "What can you help me with?"
}
]
)
# Multiple messages in a conversation
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "I need help with research"
},
{
"role": "assistant",
"content": "I can help you with that. What topic would you like to research?"
},
{
"role": "user",
"content": "Let's research quantum computing"
}
]
)
```
## Managing Session Context
```python theme={"dark"}
# Get session history
history = client.sessions.history(
session_id=session.id,
limit=10,
before=datetime.now()
)
# Update session context
session = client.sessions.update(
session_id=session.id,
metadata={"topic": "quantum computing"}
)
# Clear session history
client.sessions.clear(session_id=session.id)
```
## Session Documents
Manage documents associated with a session:
```python theme={"dark"}
# Add a document to the session
doc = client.sessions.docs.create(
session_id=session.id,
title="Research Notes",
content="Notes about quantum computing...",
metadata={"type": "notes"}
)
# Search session documents
results = client.sessions.docs.search(
session_id=session.id,
text="quantum",
metadata_filter={"type": "notes"}
)
# Delete a session document
client.sessions.docs.delete(
session_id=session.id,
doc_id=doc.id
)
```
## Session Management
```python theme={"dark"}
# List sessions
sessions = client.sessions.list(
agent_id=agent.id,
user_id=user.id, # Optional
limit=10,
offset=0
)
# Get session details
session = client.sessions.get(session_id=session.id)
# Delete a session
client.sessions.delete(session_id=session.id)
```
## Error Handling
```python theme={"dark"}
from julep.exceptions import JulepError, SessionNotFoundError
try:
session = client.sessions.get("nonexistent_id")
except SessionNotFoundError:
print("Session not found")
except JulepError as e:
print(f"An error occurred: {e}")
```
## Working with Context Overflow
Julep provides different strategies for handling context overflow:
```python theme={"dark"}
# Adaptive context handling (default)
session = client.sessions.create(
agent_id=agent.id,
context_overflow="adaptive"
)
# Truncate old messages
session = client.sessions.create(
agent_id=agent.id,
context_overflow="truncate"
)
# Summarize old messages
session = client.sessions.create(
agent_id=agent.id,
context_overflow="summarize"
)
```
# Working with Tasks
Source: https://docs.julep.ai/sdks/python/tasks
Learn how to create and manage tasks using the Python SDK
## Creating Tasks
Create a task with a specific workflow:
```python [expandable] theme={"dark"}
import yaml
task_yaml = """
name: Research Task
description: Perform research on a given topic
tools:
- name: web_search
type: integration
integration:
provider: brave
method: search
main:
- prompt:
- role: system
content: You are {{agent.name}}. {{agent.about}}
- role: user
content: Research the topic: {{_.topic}}
unwrap: true
- tool: web_search
arguments:
query: _.topic
- prompt:
- role: system
content: Summarize the research findings
- role: user
content: Here are the search results: {{_.search_results}}
"""
task = client.tasks.create(
agent_id=agent.id,
**yaml.safe_load(task_yaml)
)
```
## Task Components
A task in Julep consists of several components:
```python [expandable] theme={"dark"}
task = client.tasks.create(
agent_id=agent.id,
name="Complex Task",
description="A multi-step task with various components",
input_schema={
"type": "object",
"properties": {
"topic": {"type": "string"},
"depth": {"type": "integer", "minimum": 1, "maximum": 5}
},
"required": ["topic"]
},
tools=[
{
"name": "web_search",
"type": "integration",
"integration": {
"provider": "brave",
"method": "search"
}
}
],
main=[
{"prompt": "Research {{_.topic}} at depth {{_.depth}}"},
{"tool": "web_search", "arguments": {"query": "_.topic"}},
{"evaluate": {"results": "process_results(_)"}}
]
)
```
## Executing Tasks
Execute a task with specific inputs:
```python theme={"dark"}
# Create an execution
execution = client.executions.create(
task_id=task.id,
input={"topic": "quantum computing", "depth": 3}
)
# Check execution status
while True:
result = client.executions.get(execution.id)
if result.status in ['succeeded', 'failed']:
break
time.sleep(1)
# Get the results
if result.status == "succeeded":
print(result.output)
else:
print(f"Execution failed: {result.error}")
```
## Managing Task Executions
```python theme={"dark"}
# List executions for a task
executions = client.executions.list(
task_id=task.id,
limit=10,
offset=0,
status="succeeded" # Filter by status
)
# Cancel an execution
client.executions.cancel(execution_id=execution.id)
```
## Task Control Flow
Julep supports various control flow operations in tasks:
```yaml [expandable] theme={"dark"}
main:
# Conditional execution
- if: _.score > 0.8
then:
- log: High score achieved
else:
- log: Score needs improvement
# Iteration
- foreach:
in: _.data_list
do:
- log: "Processing {{_}}"
# Parallel processing
- map_reduce:
over: _.topics
map:
- prompt: Write about {{_}}
parallelism: 5
# Error handling
- try:
- tool: risky_operation
catch:
- log: Operation failed
```
## Error Handling
```python theme={"dark"}
from julep.exceptions import JulepError, TaskNotFoundError, ExecutionError
try:
execution = client.executions.create(task_id="nonexistent_id")
except TaskNotFoundError:
print("Task not found")
except ExecutionError as e:
print(f"Execution failed: {e}")
except JulepError as e:
print(f"An error occurred: {e}")
```
# Tools & Integration
Source: https://docs.julep.ai/sdks/python/tools-integration
Learn how to use tools and integrations with the Python SDK
## Overview
Julep supports various types of tools and integrations that can be used in your tasks:
1. Built-in integrations (e.g., web search, email)
2. User-defined functions
3. System tools
4. Direct API calls
## Built-in Integrations
### Web Search Integration
```python theme={"dark"}
# Add web search capability to an agent
client.agents.tools.create(
agent_id=agent.id,
name="web_search",
type="integration",
integration={
"provider": "brave",
"method": "search",
"setup": {
"brave_api_key": "your_brave_api_key"
}
}
)
```
### Email Integration
```python theme={"dark"}
# Add email capability
client.agents.tools.create(
agent_id=agent.id,
name="send_email",
type="integration",
integration={
"provider": "email",
"setup": {
"host": "smtp.gmail.com",
"port": 587,
"user": "your_email@gmail.com",
"password": "your_app_password"
}
}
)
```
## User-defined Functions
Create custom tools using function definitions:
```python [expandable] theme={"dark"}
# Define a custom tool
client.agents.tools.create(
agent_id=agent.id,
name="calculate_price",
type="function",
function={
"description": "Calculate total price including tax",
"parameters": {
"type": "object",
"properties": {
"base_price": {
"type": "number",
"description": "Base price before tax"
},
"tax_rate": {
"type": "number",
"description": "Tax rate as a decimal"
}
},
"required": ["base_price", "tax_rate"]
}
}
)
```
## System Tools
Use built-in system operations:
```python theme={"dark"}
# Add document management capability
client.agents.tools.create(
agent_id=agent.id,
name="manage_docs",
type="system",
system={
"resource": "agent",
"subresource": "doc",
"operation": "create"
}
)
```
## Direct API Calls
Make direct API calls from your tasks:
```python theme={"dark"}
# Add external API integration with params_schema
client.agents.tools.create(
agent_id=agent.id,
name="weather_api",
type="api_call",
api_call={
"method": "GET",
"url": "https://api.weather.com/v1/current",
"headers": {
"Authorization": "Bearer {{env.WEATHER_API_KEY}}"
},
"params_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name or coordinates"
},
"units": {
"type": "string",
"enum": ["metric", "imperial"],
"description": "Temperature units"
}
},
"required": ["location"]
}
}
)
```
## Using Tools in Tasks
Example of using tools in a task:
```yaml [expandable] theme={"dark"}
name: Research Assistant
description: Research and summarize topics
tools:
- name: web_search
type: integration
integration:
provider: brave
method: search
- name: send_email
type: integration
integration:
provider: email
- name: calculate_price
type: function
function:
parameters:
type: object
properties:
base_price:
type: number
tax_rate:
type: number
main:
- tool: web_search
arguments:
query: _.topic
- tool: calculate_price
arguments:
base_price: 100
tax_rate: 0.1
- tool: send_email
arguments:
to: _.email
subject: "Research Results"
body: _.summary
```
## Error Handling
Handle tool-specific errors:
```python theme={"dark"}
from julep.exceptions import ToolError, IntegrationError
try:
result = client.executions.create(task_id=task.id)
except ToolError as e:
print(f"Tool execution failed: {e}")
except IntegrationError as e:
print(f"Integration error: {e}")
```
## Best Practices
1. **Security**: Store sensitive credentials in environment variables or secure storage
2. **Rate Limiting**: Be aware of API rate limits for external services
3. **Error Handling**: Implement proper error handling for each tool
4. **Testing**: Test tools with sample data before using in production
5. **Documentation**: Document tool usage and requirements for team reference
# Browser Use
Source: https://docs.julep.ai/tutorials/browser-use
Learn how to use Julep browser automation capabilities
## Overview
This tutorial demonstrates how to:
* Set up browser automation with Julep
* Navigate web pages programmatically
* Execute browser actions like clicking and typing
* Process visual feedback through screenshots
* Create goal-oriented browser automation tasks
## Task Structure
Let's break down the task into its core components:
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
goal:
type: string
agent_id:
type: string
description: The id of the agent to use for the browser automation
required:
- goal
- agent_id
```
This schema specifies that our task expects a goal string describing what the browser automation should accomplish.
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml [expandable] theme={"dark"}
tools:
- name: create_browserbase_session
type: integration
integration:
provider: browserbase
method: create_session
setup:
api_key: "YOUR_BROWSERBASE_API_KEY"
project_id: YOUR_PROJECT_ID
- name: get_session_view_urls
type: integration
integration:
provider: browserbase
method: get_live_urls
- name: perform_browser_action
type: integration
integration:
provider: remote_browser
method: perform_action
setup:
width: 1024
height: 768
- name: create_julep_session
type: system
system:
resource: session
operation: create
- name: session_chat
type: system
system:
resource: session
operation: chat
```
### 3. Main Workflow Steps
```yaml theme={"dark"}
- tool: create_julep_session
arguments:
agent: $ str(agent.id)
situation: "The environment is a browser"
recall: 'False'
```
This step initializes a new Julep session for the AI agent. The session serves as a container for the conversation history and enables the agent to maintain context throughout the interaction.
```yaml theme={"dark"}
- evaluate:
julep_session_id: $ _.id
```
After creating the session, we store its unique identifier for future reference.
```yaml theme={"dark"}
- tool: create_browserbase_session
arguments:
project_id: YOUR_PROJECT_ID
```
This step establishes a new browser session using BrowserBase. It creates an isolated, headless Chrome browser instance that the agent can control.
```yaml theme={"dark"}
- evaluate:
browser_session_id: $ _.id
connect_url: $ _.connect_url
```
We store both the browser session ID and connect URL in a single evaluation step.
```yaml theme={"dark"}
- tool: get_session_view_urls
arguments:
id: $ _.browser_session_id
```
This step retrieves various URLs associated with the browser session, including debugging interfaces and live view URLs.
```yaml theme={"dark"}
- evaluate:
debugger_url: $ _.urls.debuggerUrl
```
We specifically store the debugger URL, which provides access to Chrome DevTools Protocol debugging interface.
```yaml theme={"dark"}
- tool: perform_browser_action
arguments:
connect_url: $ steps[3].output.connect_url
action: "navigate"
text: "https://www.google.com"
```
This step navigates to Google's homepage to avoid sending a blank screenshot when computer use starts.
```yaml theme={"dark"}
- workflow: run_browser
arguments:
julep_session_id: $ steps[1].output.julep_session_id
cdp_url: $ steps[3].output.connect_url
messages:
- role: "user"
content: |-
$ f"""
* You are utilising a headless chrome browser to interact with the internet.
* You can use the computer tool to interact with the browser.
* You have access to only the browser.
* You are already inside the browser.
* You can't open new tabs or windows.
* For now, rely on screenshots as the only way to see the browser.
* You can't don't have access to the browser's UI.
* YOU CANNOT WRITE TO THE SEARCH BAR OF THE BROWSER.
* + {steps[0].input.goal} + NEWLINE + """
```
Finally, we initiate the interactive browser workflow with system capabilities and user goal.
### 4. Run Browser Subworkflow
The `run_browser` subworkflow is a crucial component that handles the interactive browser automation. It consists of three main parts:
```yaml theme={"dark"}
- tool: session_chat
arguments:
session_id: $ _.julep_session_id
messages: $ _.messages
recall: $ False
- evaluate:
content: $ _.choices[0].message.content
tool_calls: |-
$ [
{
'tool_call_id': tool_call.id,
'action': load_json(tool_call.function.arguments)['action'],
'text': load_json(tool_call.function.arguments).get('text'),
'coordinate': load_json(tool_call.function.arguments).get('coordinate')
}
for tool_call in _.choices[0].message.tool_calls or [] if tool_call.type == 'function']
```
This step engages the AI agent in conversation, allowing it to:
* Process and understand the user's goal
* Plan appropriate browser actions
* Generate responses based on the current browser state
* Make decisions about next steps
```yaml theme={"dark"}
- foreach:
in: $ _.tool_calls
do:
tool: perform_browser_action
arguments:
connect_url: $ steps[0].input.cdp_url
action: $ _.action
text: $ _.get('text')
coordinate: $ _.get('coordinate')
```
This component:
* Iterates through planned actions sequentially
* Executes browser commands (navigation, clicking, typing)
* Handles different types of interactions (text input, mouse clicks)
* Captures screenshots for visual feedback
```yaml [expandable] theme={"dark"}
- evaluate:
contents: >-
$ [ \
{ \
'type': 'image_url', \
'image_url': { \
'url': result['base64_image'], \
} \
} if result['base64_image'] is not None else \
{ \
'type': 'text', \
'text': result['output'] if result['output'] is not None else 'done' \
} \
for result in _]
- evaluate:
messages: "$ [{'content': [_.contents[i]], 'role': 'tool', 'name': 'computer', 'tool_call_id': steps[1].output.tool_calls[i].tool_call_id} for i in range(len(_.contents))]"
- workflow: check_goal_status
arguments:
messages: $ _.messages
julep_session_id: $ steps[0].input.julep_session_id
cdp_url: $ steps[0].input.cdp_url
```
This final part:
* Assesses progress toward the user's goal
* Determines if additional actions are needed
* Maintains conversation context
* Decides whether to continue or conclude the workflow
### 5. Check Goal Status Subworkflow
The `check_goal_status` subworkflow is a recursive component that ensures continuous operation until the goal is achieved:
```yaml theme={"dark"}
check_goal_status:
- if: $ len(_.messages) > 0
then:
workflow: run_browser
arguments:
messages: $ _.messages
julep_session_id: $ _.julep_session_id
cdp_url: $ _.cdp_url
```
This workflow:
* Checks if there are any messages to process (`len(_.messages) > 0`)
* If messages exist, recursively calls the `run_browser` workflow
* Passes along the current session context and connection details
* Maintains the conversation flow until the goal is achieved
* Automatically terminates when no more messages need processing
This recursive pattern ensures that the browser automation continues until either:
* The goal is successfully achieved
* No more actions are needed
* An error occurs that prevents further progress
```yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Browser Use Task
description: A Julep agent that can use the computer tool to interact with the browser.
########################################################
################### INPUT SCHEMA #######################
########################################################
input_schema:
type: object
properties:
goal:
type: string
required:
- goal
########################################################
####################### TOOLS ##########################
########################################################
tools:
- name: create_browserbase_session
type: integration
integration:
provider: browserbase
method: create_session
setup:
api_key: "YOUR_BROWSERBASE_API_KEY"
project_id: "YOUR_BROWSERBASE_PROJECT_ID"
- name: get_session_view_urls
type: integration
integration:
provider: browserbase
method: get_live_urls
setup:
api_key: "YOUR_BROWSERBASE_API_KEY"
project_id: "YOUR_BROWSERBASE_PROJECT_ID"
- name: perform_browser_action
type: integration
integration:
provider: remote_browser
method: perform_action
setup:
width: 1024
height: 768
- name: create_julep_session
type: system
system:
resource: session
operation: create
- name: session_chat
type: system
system:
resource: session
operation: chat
########################################################
################### MAIN WORKFLOW ######################
########################################################
main:
# Step #0 - Create Julep Session
- tool: create_julep_session
arguments:
agent: $ str(agent.id)
situation: "Juelp Browser Use Agent"
recall: 'False'
# Step #1 - Store Julep Session ID
- evaluate:
julep_session_id: $ _.id
# Step #2 - Create Browserbase Session
- tool: create_browserbase_session
arguments:
project_id: "c35ee022-883e-4070-9f3c-89607393214b"
# Step #3 - Store Browserbase Session Info
- evaluate:
browser_session_id: $ _.id
connect_url: $ _.connect_url
# Step #4 - Get Session View URLs
- tool: get_session_view_urls
arguments:
id: $ _.browser_session_id
# Step #5 - Store Debugger URL
- evaluate:
debugger_url: $ _.urls.debuggerUrl
# Step #6 - Navigate to Google
# Navigate to google to avoid sending a blank
# screenshot when computer use starts
- tool: perform_browser_action
arguments:
connect_url: $ steps[3].output.connect_url
action: "navigate"
text: "https://www.google.com"
# Step #7 - Run Browser Workflow
- workflow: run_browser
arguments:
julep_session_id: $ steps[1].output.julep_session_id
cdp_url: $ steps[3].output.connect_url
messages:
- role: "user"
content: |-
$ f"""
* You are utilising a headless chrome browser to interact with the internet.
* You can use the computer tool to interact with the browser.
* You have access to only the browser.
* You are already inside the browser.
* You can't open new tabs or windows.
* For now, rely on screenshots as the only way to see the browser.
* You don't have access to the browser's UI.
* YOU CANNOT WRITE TO THE SEARCH BAR OF THE BROWSER.
* + {steps[0].input.goal} + NEWLINE + """
########################################################
################# RUN BROWSER SUBWORKFLOW #################
########################################################
run_browser:
# Step #0 - Agent Interaction
- tool: session_chat
arguments:
session_id: $ _.julep_session_id
messages: $ _.messages
recall: $ False
# Step #1 - Evaluate the response from the agent
- evaluate:
content: $ _.choices[0].message.content
tool_calls: |-
$ [
{
'tool_call_id': tool_call.id,
'action': load_json(tool_call.function.arguments)['action'],
'text': load_json(tool_call.function.arguments).get('text'),
'coordinate': load_json(tool_call.function.arguments).get('coordinate')
}
for tool_call in _.choices[0].message.tool_calls or [] if tool_call.type == 'function']
# Step #2 - Perform the actions requested by the agent
- foreach:
in: $ _.tool_calls
do:
tool: perform_browser_action
arguments:
connect_url: $ steps[0].input.cdp_url
action: $ _.action if not (str(_.get('text', '')).startswith('http') and _.action == 'type') else 'navigate'
text: $ _.get('text')
coordinate: $ _.get('coordinate')
# Step #3 - Convert the result of the actions into a chat message
- evaluate:
contents: >-
$ [ \
{ \
'type': 'image_url', \
'image_url': { \
'url': result['base64_image'], \
} \
} if result['base64_image'] is not None else \
{ \
'type': 'text', \
'text': result['output'] if result['output'] is not None else 'done' \
} \
for result in _]
# Step #4 - Convert the result of the actions into a chat message
- evaluate:
messages: "$ [{'content': [_.contents[i]], 'role': 'tool', 'name': 'computer', 'tool_call_id': steps[1].output.tool_calls[i].tool_call_id} for i in range(len(_.contents))]"
# Step #5 - Check if the goal is achieved and recursively run the browser
- workflow: check_goal_status
arguments:
messages: $ _.messages
julep_session_id: $ steps[0].input.julep_session_id
cdp_url: $ steps[0].input.cdp_url
########################################################
############## CHECK GOAL STATUS SUBWORKFLOW ##############
########################################################
check_goal_status:
# Step #0 - Check if the goal is achieved and recursively run the browser
- if: $ len(_.messages) > 0
then:
workflow: run_browser
arguments:
messages: $ _.messages
julep_session_id: $ _.julep_session_id
cdp_url: $ _.cdp_url
```
## Usage
Here's how to use this task with the Julep SDK:
```python Python [expandable] theme={"dark"}
from julep import Client
import yaml
import time
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
# Create the agent
agent = client.agents.create(
name="Julep Browser Use Agent",
about="A Julep agent that can use the computer tool to interact with the browser.",
)
# Load the task definition
with open('browser_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=agent.id,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={
"agent_id": agent.id,
"goal": "Search for recent news about artificial intelligence"
}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
# Print the result
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```js Node.js [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
import yaml from 'yaml';
import fs from 'fs';
// Initialize the client
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Julep Browser Use Agent",
about: "A Julep agent that can use the computer tool to interact with the browser.",
});
// Parse the task definition
const taskDefinition = yaml.parse(fs.readFileSync('browser_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
"agent_id": agent.id,
"goal": "Search for recent news about artificial intelligence"
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Print the result
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
## Key Features
* **Browser Automation**: Performs web interactions like navigation, clicking, and typing
* **Visual Feedback**: Captures screenshots to verify actions and understand page state
* **Goal-Oriented**: Continues executing actions until the user's goal is achieved
* **Secure Sessions**: Uses BrowserBase for isolated browser instances
* **Interactive Workflow**: Uses run\_browser subworkflow for continuous interaction
## Next Steps
* Try this task yourself, check out the full example, see the [browser-use cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/06-browser-use.ipynb).
* To learn more about the integrations used in this task, check out the [integrations](/integrations/supported-integrations) page.
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Tools](/concepts/tools)
# Hacker News Newsletter Generator
Source: https://docs.julep.ai/tutorials/hacker-news-newsletter
Learn how to build a personalized newsletter generator that fetches top Hacker News stories, analyzes their relevance to user interests, and creates AI-powered summaries
## Overview
This tutorial demonstrates how to:
* Fetch and filter top stories from Hacker News API
* Scrape full article content using web scraping integration
* Personalize content based on user preferences using AI
* Generate concise summaries for curated stories
* Process data in parallel for optimal performance
## Task Structure
Let's break down the task into its core components:
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
min_score:
type: integer
default: 50
num_stories:
type: integer
default: 10
description: Number of stories to include in newsletter
user_preferences:
type: array
items:
type: string
description: User's technology interests (e.g., ["AI/ML", "Python", "Startups"])
```
This schema allows users to:
* Set a minimum HN score threshold for quality filtering
* Specify how many stories to include in the final newsletter
* Define their technology interests for personalization
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: fetch_hn_stories
type: api_call
api_call:
method: GET
url: https://hacker-news.firebaseio.com/v0/topstories.json
headers:
Content-Type: application/json
- name: get_story_details
type: api_call
api_call:
method: GET
url: "https://example.com"
headers:
Content-Type: application/json
- name: get_comment_details
type: api_call
api_call:
method: GET
url: https://hacker-news.firebaseio.com/v0/item/{{comment_id}}.json
- name: spider_fetch
type: integration
integration:
provider: spider
setup:
spider_api_key: YOUR_SPIDER_API_KEY
```
We're using:
* Direct Hacker News API calls for stories and comments
* Spider integration for advanced web scraping capabilities
### 3. Main Workflow Steps
```yaml theme={"dark"}
- tool: fetch_hn_stories
arguments:
url: "https://hacker-news.firebaseio.com/v0/topstories.json"
label: fetch_story_ids
- evaluate:
story_ids: $ steps["fetch_story_ids"].output.json[:50]
message: $ f"Fetched {len(steps['fetch_story_ids'].output.json)} stories, processing top 50"
label: extract_ids
```
This step:
* Fetches the current top 500 story IDs from Hacker News
* Extracts the first 50 for processing
```yaml theme={"dark"}
- over: $ steps["extract_ids"].output["story_ids"]
parallelism: 10
map:
tool: get_story_details
arguments:
method: GET
url: $ f"https://hacker-news.firebaseio.com/v0/item/{_}.json"
label: all_stories
- evaluate:
stories: $ [item["json"] for item in _ if item and "json" in item]
label: extract_stories
```
This step:
* Fetches full details for each story ID
* Processes 10 stories in parallel for efficiency
* Extracts successfully fetched story data
```yaml theme={"dark"}
- evaluate:
filtered: $ [s for s in steps["extract_stories"]["output"]["stories"]
if "score" in s and s["score"] >= inputs.get("min_score", 50)]
label: filter_stories
- evaluate:
sorted_stories: '$ steps["filter_stories"]["output"]["filtered"][:inputs.get("num_stories", 10)]'
label: sort_stories
```
This step:
* Filters stories by minimum score threshold
* Sorts by score and takes the top N stories
* Ensures quality content for the newsletter
```yaml [expandable] theme={"dark"}
- over: $ steps["sort_stories"]["output"]["sorted_stories"]
parallelism: 4
map:
tool: spider_fetch
arguments:
url: $ _['url']
params:
request: smart_mode
return_format: markdown
proxy_enabled: $ True
filter_output_images: $ True
filter_output_svg: $ True
readability: $ True
limit: 1
label: fetch_content
- evaluate:
scraped_contents: '$ [item["result"][0]["content"] if item and "result" in item
and item["result"] and "content" in item["result"][0] else ""
for item in _]'
label: extract_scraped_content
```
* **smart\_mode**: Intelligently extracts main content
* **return\_format: markdown**: Clean, parseable text format
* **proxy\_enabled**: Avoids rate limiting and blocks
* **filter\_output\_images/svg**: Text-only content
* **readability**: Enhanced article parsing
* **parallelism: 4**: Balanced to avoid overwhelming target sites
This step:
* Scrapes full article content for each story
* Converts to clean markdown format
* Handles failed scrapes gracefully
```yaml theme={"dark"}
- evaluate:
comment_pairs: '$ [{"story_id": story["id"], "story_index": idx, "comment_id": kid}
for idx, story in enumerate(steps["sort_stories"]["output"]["sorted_stories"])
if "kids" in story for kid in story["kids"][:3]]'
label: prepare_comments
- over: '$ steps["prepare_comments"]["output"]["comment_pairs"]'
parallelism: 15
map:
tool: get_comment_details
arguments:
method: GET
url: '$ f"https://hacker-news.firebaseio.com/v0/item/{_["comment_id"]}.json"'
label: fetch_all_comments
```
This step:
* Prepares comment IDs (up to 3 per story)
* Fetches comment details with high parallelism
* Maintains story-comment relationships
```yaml [expandable] theme={"dark"}
- evaluate:
stories_with_comments: '$ [dict(story,
content=steps["extract_scraped_content"]["output"]["scraped_contents"][i],
top_comments=[item[1] for item in steps["comments_with_index"]["output"]["comments_grouped"]
if item[0] == i])
for i, story in enumerate(steps["sort_stories"]["output"]["sorted_stories"])]'
label: final_stories_with_comments
- over: $ steps["final_stories_with_comments"]["output"]["stories_with_comments"]
parallelism: 10
map:
prompt:
- role: system
content: |-
$ f'''
You are a content curator. Score this HN story's relevance to the user's interests.
User interests: {steps[0].input.user_preferences}
Return only a JSON object with the relevance score (0-100).
Return ONLY raw JSON without markdown code blocks
'''
- role: user
content: >-
$ f'''
Story to analyze:
Title: {_["title"]}
URL: {_["url"]}
Score: {_["score"]}
Content preview: {_["content"]}
Top comment: {_["top_comments"][0]["text"]}
Return format: "relevance_score" from 0 to 100
'''
unwrap: true
label: score_stories
- evaluate:
personalized_stories: $ [item for item in steps["combine_scores"]["output"]["scored_stories"]
if item["relevance_score"] >= 60]
label: filter_personalized
```
This step:
* Combines stories with their content and comments
* Uses AI to score relevance (0-100) based on user preferences
* Filters stories with relevance >= 60 for high personalization
```yaml theme={"dark"}
- over: $ steps["filter_personalized"]["output"]["personalized_stories"]
parallelism: 10
map:
prompt:
- role: system
content: |
Generate a concise, insightful summary (max 100 words) for this article.
Focus on key insights and why it matters.
- role: user
content: >-
$ f'''
Title: {_["story"]["title"]}
Content: {_["story"]["content"]}
Top comments: {_["story"]["top_comments"]}
'''
unwrap: true
label: generate_summaries
- evaluate:
final_output: |
$ [{
"title": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"]["title"],
"url": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"]["url"],
"hn_url": f"https://news.ycombinator.com/item?id={steps['filter_personalized']['output']['personalized_stories'][i]['story']['id']}",
"comments_count": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"].get("descendants", 0),
"summary": steps["generate_summaries"]["output"][i]
} for i in range(len(steps["filter_personalized"]["output"]["personalized_stories"]))]
label: prepare_final_output
```
This step:
* Generates 100-word AI summaries for each story
* Formats the final newsletter with all relevant information
* Includes both article URL and HN discussion URL
```yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: HN Newsletter Generator
description: Fetch top Hacker News stories, personalize content
input_schema:
type: object
properties:
min_score:
type: integer
default: 50
num_stories:
type: integer
default: 10
description: Number of stories to include in newsletter
user_preferences:
type: array
items:
type: string
description: User's technology interests (e.g., ["AI/ML", "Python", "Startups"])
tools:
# Fetch top story IDs from Hacker News
- name: fetch_hn_stories
type: api_call
api_call:
method: GET
url: https://hacker-news.firebaseio.com/v0/topstories.json
headers:
Content-Type: application/json
# Get detailed information for a specific story
- name: get_story_details
type: api_call
api_call:
method: GET
url: "https://example.com"
headers:
Content-Type: application/json
# Fetch individual comment details
- name: get_comment_details
type: api_call
api_call:
method: GET
url: https://hacker-news.firebaseio.com/v0/item/{{comment_id}}.json
# Spider web scraping integration
- name: spider_fetch
type: integration
integration:
provider: spider
setup:
spider_api_key: YOUR_SPIDER_API_KEY
main:
# Step 0: Fetch top story IDs from Hacker News
- tool: fetch_hn_stories
arguments:
url: "https://hacker-news.firebaseio.com/v0/topstories.json"
label: fetch_story_ids
# Step 1: Extract first 50 story IDs
- evaluate:
story_ids: $ steps["fetch_story_ids"].output.json[:50]
message: $ f"Fetched {len(steps['fetch_story_ids'].output.json)} stories, processing top 50"
label: extract_ids
# Step 2: Fetch details for each story in parallel
- over: $ steps["extract_ids"].output["story_ids"]
parallelism: 10
map:
tool: get_story_details
arguments:
method: GET
url: $ f"https://hacker-news.firebaseio.com/v0/item/{_}.json"
label: all_stories
# Step 3: Extract successfully fetched story data
- evaluate:
stories: $ [item["json"] for item in _ if item and "json" in item]
label: extract_stories
# Step 4: Filter by score
- evaluate:
filtered: $ [s for s in steps["extract_stories"]["output"]["stories"] if "score" in s and s["score"] >= inputs.get("min_score", 50)]
label: filter_stories
# Step 5: Sort stories by score and limit
- evaluate:
sorted_stories: '$ steps["filter_stories"]["output"]["filtered"][:inputs.get("num_stories", 10)]'
label: sort_stories
# Step 6: Fetch full article content using Spider
- over: $ steps["sort_stories"]["output"]["sorted_stories"]
parallelism: 4
map:
tool: spider_fetch
arguments:
url: $ _['url']
params:
request: smart_mode
return_format: markdown
proxy_enabled: $ True
filter_output_images: $ True
filter_output_svg: $ True
readability: $ True
limit: 1
label: fetch_content
# Step 7: Extract scraped content
- evaluate:
scraped_contents: '$ [item["result"][0]["content"] if item and "result" in item and item["result"] and "content" in item["result"][0] else "" for item in _]'
label: extract_scraped_content
# Step 8: Prepare comment fetching
- evaluate:
comment_pairs: '$ [{"story_id": story["id"], "story_index": idx, "comment_id": kid} for idx, story in
enumerate(steps["sort_stories"]["output"]["sorted_stories"]) if "kids" in story for kid in story["kids"][:3]]'
label: prepare_comments
# Step 9: Fetch all comment details
- over: '$ steps["prepare_comments"]["output"]["comment_pairs"]'
parallelism: 15
map:
tool: get_comment_details
arguments:
method: GET
url: '$ f"https://hacker-news.firebaseio.com/v0/item/{_["comment_id"]}.json"'
label: fetch_all_comments
# Step 10: Extract comment data
- evaluate:
comment_results: '$ [item["json"] for item in _ if item and "json" in item and item["json"]]'
label: extract_comments
# Step 11: Group comments by story
- evaluate:
comments_grouped: '$ [[pair["story_index"], steps["extract_comments"]["output"]["comment_results"][i]] for i, pair in
enumerate(steps["prepare_comments"]["output"]["comment_pairs"])]'
label: comments_with_index
# Step 12: Combine stories with content and comments
- evaluate:
stories_with_comments: '$ [dict(story, content=steps["extract_scraped_content"]["output"]["scraped_contents"][i], top_comments=[item[1] for item in
steps["comments_with_index"]["output"]["comments_grouped"] if item[0] == i]) for i, story in enumerate(steps["sort_stories"]["output"]["sorted_stories"])]'
label: final_stories_with_comments
# Step 13: Score stories based on user preferences
- over: $ steps["final_stories_with_comments"]["output"]["stories_with_comments"]
parallelism: 10
map:
prompt:
- role: system
content: |-
$ f'''
You are a content curator. Score this HN story's relevance to the user's interests.
User interests: $ {{ steps[0].input.user_preferences }}
Return only a JSON object with the relevance score (0-100).
Return ONLY raw JSON without markdown code blocks
'''
- role: user
content: >-
$ f'''
Story to analyze:
Title: $ {{ _["title"] }}
URL: $ {{ _["url"] }}
Score: $ {{ _["score"] }}
Content preview: $ {{ _["content"]}}
Top comment: $ {{ _["top_comments"][0]["text"] }}
Return format: "relevance_score" from 0 to 100
'''
unwrap: true
label: score_stories
# Step 14: Combine with scores
- evaluate:
scored_stories: '$ [{"story": steps["final_stories_with_comments"]["output"]["stories_with_comments"][i], "relevance_score": json.loads(steps["score_stories"]["output"][i])["relevance_score"]} for i in range(len(steps["score_stories"]["output"]))]'
label: combine_scores
# Step 15: Filter by relevance
- evaluate:
personalized_stories: $ [item for item in steps["combine_scores"]["output"]["scored_stories"] if item["relevance_score"] >= 60]
label: filter_personalized
# Step 16: Generate summaries
- over: $ steps["filter_personalized"]["output"]["personalized_stories"]
parallelism: 10
map:
prompt:
- role: system
content: |
Generate a concise, insightful summary (max 100 words) for this article.
Focus on key insights and why it matters.
- role: user
content: >-
$ f'''
Title: {{ _["story"]["title"] }}
Content: {{ _["story"]["content"] }}
Top comments: {{ _["story"]["top_comments"] }}
'''
unwrap: true
label: generate_summaries
# Step 17: Prepare final output
- evaluate:
final_output: |
$ [{
"title": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"]["title"],
"url": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"]["url"],
"hn_url": f"https://news.ycombinator.com/item?id={{steps['filter_personalized']['output']['personalized_stories'][i]['story']['id']}}",
"comments_count": steps["filter_personalized"]["output"]["personalized_stories"][i]["story"].get("descendants", 0),
"summary": steps["generate_summaries"]["output"][i]
} for i in range(len(steps["filter_personalized"]["output"]["personalized_stories"]))]
label: prepare_final_output
```
## Usage
Here's how to use this task with the Julep SDK:
```python Python [expandable] theme={"dark"}
from julep import Client
import time
import yaml
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
# Create the agent
agent = client.agents.create(
name="Hacker News Agent",
about="A hacker news agent that can fetch the top stories from Hacker News and summarize them.",
model="gpt-4o"
)
# Load the task definition
with open('hn_newsletter_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=agent.id,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={
"min_score": 100,
"num_stories": 5,
"user_preferences": ["AI/ML", "Python", "DevOps", "Cloud Computing"]
}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(5)
# Print the result
if result.status == "succeeded":
for story in result.output['final_output']:
print(f"Title: {story['title']}")
print(f"URL: {story['url']}")
print(f"HN Discussion: {story['hn_url']}")
print(f"Comments: {story['comments_count']}")
print(f"Summary: {story['summary']}")
print("-" * 80)
else:
print(f"Error: {result.error}")
```
```js Node.js [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
import fs from 'fs';
import yaml from 'yaml';
// Initialize the client
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Hacker News Agent",
about: "A hacker news agent that can fetch the top stories from Hacker News and summarize them.",
model: "gpt-4o"
});
// Load the task definition
const taskDefinition = yaml.parse(fs.readFileSync('hn_newsletter_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
min_score: 100,
num_stories: 5,
user_preferences: ["AI/ML", "Python", "DevOps", "Cloud Computing"]
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 5000));
}
// Print the result
if (result.status === 'succeeded') {
result.output.final_output.forEach(story => {
console.log(`Title: ${story.title}`);
console.log(`URL: ${story.url}`);
console.log(`HN Discussion: ${story.hn_url}`);
console.log(`Comments: ${story.comments_count}`);
console.log(`Summary: ${story.summary}`);
console.log('-'.repeat(80));
});
} else {
console.error(`Error: ${result.error}`);
}
```
## Example Output
An example output when running this task with user preferences for AI/ML and Python:
**Title:** OpenAI Announces GPT-5 with Revolutionary Reasoning Capabilities\
**URL:** [https://openai.com/research/gpt-5](https://openai.com/research/gpt-5)\
**HN Discussion:** [https://news.ycombinator.com/item?id=12345678](https://news.ycombinator.com/item?id=12345678)\
**Comments:** 234\
**Summary:** OpenAI's GPT-5 demonstrates unprecedented reasoning abilities and multimodal understanding. The model shows significant improvements in code generation, mathematical reasoning, and real-world problem solving. Key breakthrough involves new architecture allowing dynamic computation allocation based on task complexity. Community discusses implications for AI safety and potential applications in scientific research.
***
**Title:** Python 3.13 Released with Major Performance Improvements\
**URL:** [https://python.org/downloads/release/python-313](https://python.org/downloads/release/python-313)\
**HN Discussion:** [https://news.ycombinator.com/item?id=12345679](https://news.ycombinator.com/item?id=12345679)\
**Comments:** 156\
**Summary:** Python 3.13 brings 40% performance improvements through adaptive bytecode specialization and improved memory management. New features include better error messages, enhanced typing support, and native WASM compilation. Developers report significant speedups in data processing workloads. Discussion highlights compatibility concerns with popular libraries and migration strategies for large codebases.
***
**Title:** New ML Framework Achieves 10x Training Speed on Consumer GPUs\
**URL:** [https://github.com/fastML/framework](https://github.com/fastML/framework)\
**HN Discussion:** [https://news.ycombinator.com/item?id=12345680](https://news.ycombinator.com/item?id=12345680)\
**Comments:** 189\
**Summary:** FastML framework enables training large language models on consumer hardware through innovative gradient compression and distributed computing techniques. Benchmarks show 10x speedup compared to PyTorch for specific workloads. Framework supports automatic mixed precision and memory-efficient attention mechanisms. Community excited about democratizing ML research but debates production readiness.
## Monitoring Execution
Track the execution progress and debug issues:
```python theme={"dark"}
# Get execution transitions
transitions = client.executions.transitions.list(execution.id).items
for i, transition in enumerate(transitions):
print(f"Step {i}: {transition.type}")
if transition.type == "step":
print(f"Label: {transition.current.label}")
print(f"Status: {transition.status}")
if transition.status == "failed":
print(f"Error: {transition.error}")
print("-" * 40)
```
## Customization Ideas
1. **Email Integration**: Add email sending to deliver newsletters automatically
2. **Scheduling**: Set up periodic execution for daily/weekly newsletters
## Next Steps
* Try this task yourself, check out the full example in the [Hacker News cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/11-hacker-news.ipynb)
* Learn more about the [Spider integration](/integrations/spider) for web scraping
* Explore [parallel processing patterns](/advanced/types-of-task-steps#parallel-execution) in Julep
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Tools](/concepts/tools)
* [Integrations](/integrations/supported-integrations)
* [Python Expressions](/advanced/python-expression)
# Building a RAG-powered AI Assistant with Julep
Source: https://docs.julep.ai/tutorials/julep-assistant
Learn how to build an intelligent AI support assistant using Julep with document indexing, RAG capabilities, and a chat interface
## Overview
This tutorial demonstrates how to build a production-ready AI assistant using Julep. We'll create an intelligent support assistant that can:
* Crawl and index documentation automatically
* Answer questions using RAG (Retrieval-Augmented Generation)
* Provide contextual, accurate responses based on indexed content
* Offer an interactive chat interface with session management
* Collect and validate user feedback for continuous improvement
## What You'll Learn
By the end of this tutorial, you'll understand how to:
1. Configure a Julep agent with specific instructions and capabilities
2. Create complex workflows for document processing and indexing
3. Implement RAG-powered conversations with hybrid search
4. Build an interactive chat interface using Chainlit
5. Deploy a production-ready AI assistant
## Prerequisites
* Python 3.8+
* Julep API key (get one at [platform.julep.ai](https://platform.julep.ai))
* Basic understanding of Julep concepts (agents, tasks, sessions)
* Spider API key for web crawling
## Project Structure
The Julep Assistant project is organized as follows:
```bash theme={"dark"}
julep-assistant/
├── agent.yaml # Agent configuration
├── task/ # Julep task definitions
│ ├── main.yaml # Main workflow task
│ ├── crawl.yaml # Web crawling sub-task
│ └── full_task.yaml # Complete task with all steps
├── scripts/ # Utility scripts
│ ├── crawler.py # Standalone web crawler
│ └── indexer.py # Document indexing utility
├── chainlit-ui/ # Web interface
│ ├── app.py # Main Chainlit application
│ ├── feedback/ # Feedback handling system
│ └── requirements.txt # Python dependencies
└── julep-assistant-notebook.ipynb # Interactive notebook demo
```
## Step 1: Agent Configuration
First, let's understand how the agent is configured. The `agent.yaml` file defines the assistant's personality and capabilities:
```yaml theme={"dark"}
name: Julep Support Assistant
about: >-
You are the official Julep AI support assistant. You help developers
understand and use the Julep platform effectively.
model: claude-sonnet-4
instructions: |-
You are the official Julep AI support assistant. Your purpose is to help
developers build AI applications using the Julep platform.
Your core responsibilities:
1. **Workflow Assistance**: Help users write, debug, and optimize Julep workflows
2. **Concept Explanation**: Clearly explain Julep concepts like agents, tasks, sessions, and tools
3. **Code Examples**: Provide working code examples in Python, YAML, or JavaScript
4. **API Guidance**: Help users understand and use Julep's API effectively
5. **Best Practices**: Share proven patterns and architectural recommendations
Guidelines:
- Always provide accurate, up-to-date information from the official documentation
- Include code examples whenever possible
- Use proper syntax highlighting for code blocks
- Explain the "why" behind recommendations, not just the "how"
```
Key points:
* The agent uses Claude Sonnet 4 for high-quality responses
* Instructions provide clear guidance on how to help users
* The agent is specialized for Julep-specific support
## Step 2: Web Crawling and Document Indexing
The assistant's knowledge base is built in two stages: first crawling documentation websites, then indexing the content for RAG retrieval.
### Web Crawling with Spider Integration
Before indexing documents, we need to crawl the target website. The `task/crawl.yaml` defines a reusable crawling workflow:
```yaml theme={"dark"}
name: Julep Documentation Crawler Task
description: A Julep agent that can crawl the Julep documentation website and store the content in the document store with proper contextualization.
input_schema:
type: object
properties:
url:
type: string
description: "The URL of the documentation page"
required:
- url
tools:
- name: spider_crawler
type: integration
integration:
provider: spider
setup:
spider_api_key: {spider_api_key}
main:
- tool: spider_crawler
arguments:
url: $ _['url']
params:
request: smart_mode
return_format: markdown
proxy_enabled: $ True
filter_output_images: $ True
filter_output_svg: $ True
readability: $ True
```
### Complete Workflow: Crawl + Index
The `task/full_task.yaml` combines both crawling and indexing into a single workflow:
```yaml theme={"dark"}
main:
# Step 0: Crawl the Julep documentation using Spider (will crawl multiple pages)
- tool: spider_crawler
arguments:
url: $ _['url']
params:
request: smart_mode
limit: 2
return_format: markdown
proxy_enabled: $ True
filter_output_images: $ True
filter_output_svg: $ True
readability: $ True
# Step 1: Process each crawled page individually
- over: $ [page for page in _.result if page.status == 200 and page.content]
parallelism: 5
map:
workflow: process_single_page
arguments:
url: $ _.url
content: $ _.content
```
The key Spider crawler parameters:
* `smart_mode`: Intelligently navigates and extracts content
* `limit`: Number of pages to crawl (set to 2 for testing, increase for production)
* `return_format: markdown`: Returns clean markdown content
* `proxy_enabled`: Uses proxy for better reliability
* `filter_output_images/svg`: Removes images to focus on text content
* `readability`: Extracts main content, removing navigation and ads
### Document Indexing Workflow
After crawling, the main workflow in `task/main.yaml` processes and indexes the content:
### Input Schema
```yaml theme={"dark"}
input_schema:
type: object
properties:
url:
type: string
description: "The URL of the documentation page"
content:
type: string
description: "The markdown content of the documentation page"
required:
- url
- content
```
### Document Processing Steps
The workflow starts by creating documentation-sized chunks:
```yaml theme={"dark"}
- evaluate:
chunks: |
$ [" ".join(_.content.strip().split()[i:i + 1500])
for i in range(0, len(_.content.strip().split()), 1200)]
```
This creates \~1500 word chunks with 300-word overlap to preserve context.
Each page is analyzed to extract structured information:
```yaml theme={"dark"}
- prompt:
- role: system
content: |-
Analyze the documentation content and extract:
- primary_concepts: Which Julep concepts does this content cover?
- content_type: (tutorial, api_reference, concept_explanation, etc.)
- key_topics: Main topics discussed
- code_examples: Whether it contains code examples
- use_cases: Practical applications mentioned
```
All code examples are extracted and categorized:
```yaml theme={"dark"}
- prompt:
- role: system
content: |-
Extract all code examples from the documentation content.
For each code example found, identify:
- language: The programming language
- code: The actual code content
- purpose: What this code demonstrates
- context: When to use this code
```
For each chunk, the system generates questions and answers:
```yaml theme={"dark"}
- prompt:
- role: system
content: |-
Generate 3-5 relevant questions users might ask about this chunk
Provide clear, concise answers based on the content
Add contextual information to improve search retrieval
```
Finally, enhanced content is stored as agent documents:
```yaml theme={"dark"}
- tool: create_agent_doc
arguments:
agent_id: $ str(agent.id)
data:
metadata:
source: "spider_crawler"
url: $ steps[0].input.page_url
content_type: $ steps[2].output.doc_analysis.get('content_type')
concepts: $ steps[2].output.doc_analysis.get('primary_concepts')
content: $ _["final_content"]
```
## Step 3: Building the Chat Interface
The chat interface is built with Chainlit, providing a smooth user experience. Here's how it works:
### Session Initialization
```python theme={"dark"}
@cl.on_chat_start
async def on_chat_start():
"""Initialize a new chat session"""
# Create session with RAG search options
session = await julep_client.sessions.create(
agent=AGENT_UUID,
recall_options={
"mode": "hybrid", # Uses both vector and text search
"confidence": 0.7, # Confidence threshold
"limit": 10, # Max number of results
"embed_text": True # Embed query for vector search
}
)
# Store session for later use
cl.user_session.set("session", session)
```
### Message Handling
```python theme={"dark"}
@cl.on_message
async def on_message(message: cl.Message):
"""Handle incoming user messages"""
# Get the session
session = cl.user_session.get("session")
# Send message to Julep and stream response
msg = cl.Message(content="")
response = await julep_client.sessions.chat(
session_id=session.id,
message={
"role": "user",
"content": message.content
},
stream=True
)
# Stream tokens to user
async for chunk in response:
if chunk.choices[0].delta.content:
await msg.stream_token(chunk.choices[0].delta.content)
await msg.send()
```
## Step 4: RAG Configuration
The assistant uses hybrid search for optimal retrieval:
```python theme={"dark"}
recall_options={
"mode": "hybrid", # Combines vector and text search
"confidence": 0.7, # Minimum confidence score
"limit": 10, # Maximum results to retrieve
"embed_text": True # Enable embeddings for vector search
}
```
### Search Modes Explained
* **Hybrid Mode**: Combines semantic vector search with keyword matching
* **Vector Mode**: Pure semantic search based on embeddings
* **Text Mode**: Traditional keyword-based search
## Step 5: Dynamic Feedback System
The assistant implements an innovative feedback system that dynamically improves the agent's behavior by updating its instructions in real-time based on validated user feedback.
### How the Feedback System Works
The feedback system validates and applies user feedback directly to the agent's instructions:
```python theme={"dark"}
class FeedbackHandler:
async def process_feedback(
self,
feedback_text: str,
user_question: str,
agent_response: str,
session_id: str
) -> Dict[str, Any]:
"""Process user feedback and update agent instructions if valid"""
# Get current agent details
agent = await self.client.agents.get(agent_id=self.agent_id)
# Validate the feedback using AI
validation_result = await self.validator.validate_feedback(
feedback_text=feedback_text,
user_question=user_question,
agent_response=agent_response,
agent_instructions=current_instructions_str
)
# If feedback is valid with high confidence (>= 0.7)
if validation_result.get("is_valid") and validation_result.get("confidence", 0) >= 0.7:
updated_instructions = validation_result.get("updated_instructions")
if updated_instructions:
# Update the agent with new instructions
await self.client.agents.create_or_update(
agent_id=self.agent_id,
name=agent.name,
instructions=updated_instructions,
)
```
### Feedback Validation Process
The system uses AI to validate feedback before applying it:
```python theme={"dark"}
class FeedbackValidator:
async def validate_feedback(self, feedback_text, user_question, agent_response, agent_instructions):
"""Validate feedback using AI to ensure it's constructive and applicable"""
# AI validates if feedback is:
# 1. Constructive and specific
# 2. Relevant to improving the agent
# 3. Not contradicting core functionality
# 4. Actionable for instruction updates
# Returns:
# - is_valid: boolean
# - confidence: 0-1 score
# - category: type of feedback
# - updated_instructions: new instructions if applicable
```
### Feedback Collection UI
The system provides three feedback options:
```python theme={"dark"}
def create_feedback_actions(self, message_id: str) -> list:
"""Create Chainlit actions for feedback collection"""
return [
cl.Action(
name="feedback_helpful",
payload={"value": "helpful"},
label="👍 Helpful"
),
cl.Action(
name="feedback_not_helpful",
payload={"value": "not_helpful"},
label="👎 Not Helpful"
),
cl.Action(
name="feedback_detailed",
payload={"value": "detailed"},
label="💭 Give Detailed Feedback"
)
]
```
### Real-time Agent Improvement
When valid feedback is received, the agent immediately adapts:
1. **Positive Feedback**: Reinforces current behavior patterns
2. **Negative Feedback**: Prompts for specifics and adjusts instructions
3. **Detailed Feedback**: Allows comprehensive improvements
Example of instruction evolution:
```python theme={"dark"}
# Original instruction
"Provide code examples when explaining concepts"
# After user feedback: "The code examples are too basic"
"Provide comprehensive code examples with edge cases and best practices when explaining concepts"
```
### Benefits of Dynamic Feedback
1. **Continuous Learning**: Agent improves with each interaction
2. **User-Driven Evolution**: Adapts to actual user needs
3. **Quality Control**: AI validation prevents harmful changes
4. **Immediate Impact**: Changes apply to next interaction
This approach makes the assistant truly adaptive, learning from user interactions to provide increasingly better support over time.
## Step 6: Running the Assistant
### Installation
1. Clone the repository and install dependencies:
```bash theme={"dark"}
cd julep-assistant
pip install -r chainlit-ui/requirements.txt
```
2. Set up environment variables:
```bash theme={"dark"}
# Create .env file
JULEP_API_KEY=your_julep_api_key_here
AGENT_UUID=your_agent_uuid # Or use the default
SPIDER_API_KEY=your_spider_api_key # Optional
```
### Running the Chat Interface
```bash theme={"dark"}
cd chainlit-ui
chainlit run app.py
```
This starts the web interface at `http://localhost:8000`.
### Using Scripts for Better Monitoring
While the `full_task.yaml` can handle both crawling and indexing, using the separate scripts provides better visibility and control:
**Web Crawler Script:**
```bash theme={"dark"}
python scripts/crawler.py --url https://docs.julep.ai --max-pages 100
```
This script:
* Provides real-time progress updates
* Saves crawled content to JSON for inspection
* Allows you to verify content before indexing
* Handles rate limiting and retries
**Document Indexer Script:**
```bash theme={"dark"}
python scripts/indexer.py
```
This script:
* Reads the crawled content from the crawler output
* Shows progress for each document being indexed
* Provides detailed error messages if indexing fails
* Generates a summary report of indexed documents
**Monitoring Task Execution:**
You can also monitor the full workflow execution:
```python theme={"dark"}
# Execute the full crawl + index task
execution = await julep_client.tasks.execute(
task_id=TASK_ID,
input={
"url": "https://docs.julep.ai",
"max_pages": 100
}
)
# Monitor execution status
while execution.status in ["pending", "running"]:
execution = await julep_client.executions.get(execution.id)
print(f"Status: {execution.status}")
# Get execution steps for detailed progress
steps = await julep_client.executions.steps.list(execution.id)
for step in steps:
print(f" Step {step.name}: {step.status}")
await asyncio.sleep(5)
```
## Resources
* [Julep Assistant GitHub Repository](https://github.com/julep-ai/julep-assistant) - Complete source code and examples
* [Julep Documentation](https://docs.julep.ai)
* [API Reference](https://api.julep.ai/docs)
* [Discord Community](https://discord.com/invite/JTSBGRZrzj)
## Summary
You've learned how to build a production-ready AI assistant with Julep that features:
* Web crawling with Spider integration for content acquisition
* Automated documentation processing and indexing
* RAG-powered responses with hybrid search
* Interactive chat interface with session management
* Feedback collection and analysis using Julep documents
* Scalable architecture for production deployment
This assistant demonstrates the power of Julep for building stateful, context-aware AI applications that can maintain conversations, access external knowledge, and provide accurate, helpful responses.
# RAG Chatbot for Website
Source: https://docs.julep.ai/tutorials/rag-chatbot
Learn how to build a RAG chatbot for a website with Julep
## Overview
This tutorial demonstrates how to:
* Set up a web crawler using Julep's Spider integration
* Process and store crawled content in a document store
* Implement RAG for enhanced AI responses
* Create an intelligent agent that can answer questions about crawled content
## Task Structure
Let's break down the task into its core components:
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
url:
type: string
reducing_strength:
type: integer
```
This schema specifies that our task expects:
* A URL string (e.g., "[https://en.wikipedia.org/wiki/Artificial\_intelligence](https://en.wikipedia.org/wiki/Artificial_intelligence)")
* Number of sentences to club together to reduce the size of the chunks list (e.g., 5)
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: get_page
type: api_call
api_call:
method: GET
url: https://r.jina.ai/
headers:
accept: application/json
x-return-format: markdown
x-with-images-summary: "true"
x-with-links-summary: "true"
x-retain-images: "none"
x-no-cache: "true"
Authorization: "Bearer JINA_API_KEY"
- name: create_agent_doc
type: system
system:
resource: agent
subresource: doc
operation: create
```
We're using two tools:
* The `get_page` api call for web crawling
* The `create_agent_doc` system tool for storing processed content
### 3. Main Workflow Steps
```yaml theme={"dark"}
- tool: get_page
arguments:
url: $ "https://r.jina.ai/" + steps[0].input.url
- evaluate:
result: $ chunk_doc(_.json.data.content.strip())
- workflow: index_page
arguments:
content: $ _.result
document: $ steps[0].output.json.data.content.strip()
reducing_strength: $ steps[0].input.reducing_strength
```
The `_` variable refers to the current context object. When accessing properties like `_['url']`, it's retrieving values from the input parameters passed to the task.
This step:
* Takes the input URL and crawls the website
* Processes content into readable markdown format
* Chunks content into manageable segments
* Filters out unnecessary elements like images and SVGs
```yaml [expandable] theme={"dark"}
- evaluate:
document: $ _.document
chunks: |
$ [" ".join(_.content[i:i + max(_.reducing_strength, len(_.content) // 9)])
for i in range(0, len(_.content), max(_.reducing_strength, len(_.content) // 9))]
label: docs
# Step 1: Create a new document and add it to the agent docs store
- over: $ [(steps[0].input.document, chunk.strip()) for chunk in _.chunks]
parallelism: 3
map:
prompt:
- role: user
content: >-
$ f'''
{_[0]}
Here is the chunk we want to situate within the whole document
{_[1]}
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else.
'''
unwrap: true
settings:
max_tokens: 16000
- evaluate:
final_chunks: |
$ [
NEWLINE.join([succint, chunk.strip()]) for chunk, succint in zip(steps['docs'].output.chunks, _)
]
```
This step:
* Processes each content chunk in parallel
* Generates contextual metadata for improved retrieval
* Prepares content for storage
```yaml theme={"dark"}
- over: $ _['final_chunks']
parallelism: 3
map:
tool: create_agent_doc
arguments:
agent_id: $ agent.id
data:
metadata:
source: jina_crawler
title: Website Document
content: $ _
```
This step:
* Stores processed content in the document store
* Adds metadata for source tracking
* Creates searchable documents for RAG
```yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Jina Crawler Task
description: A Julep agent that can crawl a website and store the content in the document store.
########################################################
################### INPUT SCHEMA #######################
########################################################
input_schema:
type: object
properties:
url:
type: string
reducing_strength:
type: integer
########################################################
################### TOOLS ##############################
########################################################
tools:
- name: get_page
type: api_call
api_call:
method: GET
url: https://r.jina.ai/
headers:
accept: application/json
x-return-format: markdown
x-with-images-summary: "true"
x-with-links-summary: "true"
x-retain-images: "none"
x-no-cache: "true"
Authorization: "Bearer JINA_API_KEY"
- name : create_agent_doc
description: Create an agent doc
type: system
system:
resource: agent
subresource: doc
operation: create
########################################################
################### INDEX PAGE SUBWORKFLOW ##############
########################################################
index_page:
# Step #0 - Evaluate the content
- evaluate:
document: $ _.document
chunks: |
$ [" ".join(_.content[i:i + max(_.reducing_strength, len(_.content) // 9)])
for i in range(0, len(_.content), max(_.reducing_strength, len(_.content) // 9))]
label: docs
# Step #1 - Process each content chunk in parallel
- over: "$ [(steps[0].input.content, chunk) for chunk in _['chunks']]"
parallelism: 3
map:
prompt:
- role: user
content: >-
$ f'''
{_[0]}
Here is the chunk we want to situate within the whole document
{_[1]}
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else.'''
unwrap: true
settings:
max_tokens: 16000
# Step #2 - Create a new document and add it to the agent docs store
- evaluate:
final_chunks: |
$ [
NEWLINE.join([chunk, succint]) for chunk, succint in zip(steps[1].input.chunks, _)
]
# Step #3 - Create a new document and add it to the agent docs store
- over: $ _['final_chunks']
parallelism: 3
map:
tool: create_agent_doc
arguments:
agent_id: "$ str(agent.id)" # <--- This is the agent id of the agent you want to add the document to
data:
metadata:
source: "jina_crawler"
title: "Website Document"
content: $ _
########################################################
################### MAIN WORKFLOW ######################
########################################################
main:
# Step 0: Get the content of the product page
- tool: get_page
arguments:
url: $ "https://r.jina.ai/" + steps[0].input.url
# Step 1: Chunk the content
- evaluate:
result: $ chunk_doc(_.json.data.content.strip())
# Step 2: Evaluate step to document chunks
- workflow: index_page
arguments:
content: $ _.result
document: $ steps[0].output.json.data.content.strip()
reducing_strength: $ steps[0].input.reducing_strength
```
## Usage
Start by creating an execution for the task. This execution will make the agent crawl the website and store the content in the document store.
```python Python [expandable] theme={"dark"}
from julep import Client
import time
import yaml
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
# Create the agent
agent = client.agents.create(
name="Julep Jina Crawler Agent",
about="A Julep agent that can crawl a website and store the content in the document store.",
)
# Load the task definition
with open('crawling_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=agent.id,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={"url": "https://en.wikipedia.org/wiki/Artificial_intelligence, "reducing_strength": 5}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```js Node.js [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
import fs from 'fs';
import yaml from 'yaml';
// Initialize the client
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Julep Crawler Agent",
about: "A Julep agent that can crawl a website and store the content in the document store.",
});
// Load the task definition
const taskDefinition = yaml.parse(fs.readFileSync('crawling_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
"url": "https://en.wikipedia.org/wiki/Artificial_intelligence",
"reducing_strength": 5
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Print the result
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
Next, create a session for the agent. This session will be used to chat with the agent.
```python Python theme={"dark"}
session = client.sessions.create(
agent_id=AGENT_ID
)
```
```js Node.js theme={"dark"}
const session = await client.sessions.create({
agentId: 'YOUR_AGENT_ID'
});
```
Finally, chat with the agent.
```python Python theme={"dark"}
response = client.sessions.chat(
session_id=session.id,
messages=[
{
"role": "user",
"content": "tell me about artificial intelligence"
}
]
)
print(response)
```
```js Node.js theme={"dark"}
const response = await client.sessions.chat({
sessionId: 'YOUR_SESSION_ID',
messages: [{ role: 'user', content: 'tell me about artificial intelligence' }]
});
```
## Example Output
This is an example output when the agent is asked "What is Julep?"
Julep is a comprehensive platform designed for creating production-ready AI systems and agents. Here are the key aspects of Julep:
Core Features:
1. Complete Infrastructure Layer
* Provides infrastructure between LLMs and software
* Built-in support for long-term memory
* Multi-step process management
* State management capabilities
2. AI Agent Development
* Creates persistent AI agents that remember past interactions
* Supports complex task execution
* Enables multi-step workflows
* Includes built-in tools and integrations
3. Production-Ready Features
* Automatic retries for failed steps
* Message resending capabilities
* Task recovery systems
* Real-time monitoring
* Error handling
* Automatic scaling and load balancing
4. Development Approach
* Uses 8-Factor Agent methodology
* Treats prompts as code with proper versioning
* Provides clear tool interfaces
* Offers model independence to avoid vendor lock-in
* Includes structured reasoning capabilities
* Maintains ground truth examples for validation
Available Resources:
* Documentation: [https://docs.julep.ai/](https://docs.julep.ai/)
* API Playground: [https://dev.julep.ai/api/docs](https://dev.julep.ai/api/docs)
* Python SDK: [https://github.com/julep-ai/python-sdk/blob/main/README.md](https://github.com/julep-ai/python-sdk/blob/main/README.md)
* JavaScript SDK: [https://github.com/julep-ai/node-sdk/blob/main/README.md](https://github.com/julep-ai/node-sdk/blob/main/README.md)
* Various use case examples and cookbooks
You can explore different use cases through their cookbooks, including:
* User Profiling
* Email Assistant
* Trip Planner
* Document Management
* Website Crawler
* Multi-step Tasks
* Advanced Chat Interactions
For additional support or to learn more:
* Discord Community: [https://discord.gg/2EUJzJU2Yt](https://discord.gg/2EUJzJU2Yt)
* Book a Demo: [https://calendly.com/ishita-julep](https://calendly.com/ishita-julep)
* Dev Support: [hey@julep.ai](mailto:hey@julep.ai)
## Next Steps
* Try this task yourself, check out the full example, see the [RAG Chatbot cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/08-rag-chatbot.ipynb).
* To learn more about the integrations used in this task, check out the [integrations](/integrations/supported-integrations) page.
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Tools](/concepts/tools)
# Trip Planning
Source: https://docs.julep.ai/tutorials/trip-planning
Learn how to create a task that generates personalized travel itineraries based on weather conditions and tourist attractions for multiple locations
## Overview
The Trip Planning tutorial shows how to:
* Execute workflow steps in parallel
* Integrate with external APIs
* Combine data from multiple sources
* Generate personalized itineraries using AI
Follow each step of the tutorial:
Let's break down the task into its core components:
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
```
This schema specifies that our task expects an array of location strings (e.g., `["New York", "London", "Paris"]`).
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: YOUR_OPENWEATHERMAP_API_KEY
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: YOUR_BRAVE_API_KEY
```
We're using two integrations:
* The `weather` integration to fetch current weather conditions
* The `brave` search integration to find tourist attractions
### 3. Main Workflow Steps
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
```
When used inside a `map` or a `foreach` step, the `_` variable is a reference to the current value in the iteration.
For example:
```yaml theme={"dark"}
- over: $ ["Paris", "London"]
map:
tool: weather
arguments:
location: $ _ # _ will be "Paris", then "London"
```
This step:
* Iterates over each location in the input array
* Calls the weather API for each location
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
```
This step:
* Iterates over the locations again
* Searches for tourist attractions in each location
* Constructs a search query by concatenating "tourist attractions in " with the location
```yaml theme={"dark"}
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
```
This step:
* Combines the data from previous steps into tuples
* Each tuple contains: (location, weather\_data, attractions\_data)
* Uses Python's `zip` function to align the data
```yaml [expandable] theme={"dark"}
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
```
This step:
* Processes up to 3 locations in parallel
* For each location tuple:
* Sends a prompt to the AI model
* Includes location, weather, and attraction data
* Generates a personalized itinerary
```yaml theme={"dark"}
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
```
This step:
* Combines all itineraries into a single output
* Separates each itinerary with a divider
```yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
########################################################
################### INPUT SCHEMA #######################
########################################################
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
########################################################
################### TOOLS ##############################
########################################################
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
########################################################
################### MAIN WORKFLOW ######################
########################################################
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
```
## Usage
Here's how to use this task with the Julep SDK:
```python Python [expandable] theme={"dark"}
from julep import Client
import time
import yaml
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
# Create the agent
agent = client.agents.create(
name="Julep Trip Planning Agent",
about="A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.",
)
# Load the task definition
with open('trip_planning_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=AGENT_ID,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={
"locations": ["New York", "London", "Paris", "Tokyo", "Sydney"]
}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
# Print the result
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```js Node.js [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
import fs from 'fs';
import yaml from 'yaml';
// Initialize the client
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Julep Trip Planning Agent",
about: "A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.",
});
// Load the task definition
const taskDefinition = yaml.parse(fs.readFileSync('trip_planning_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
"locations": ["New York", "London", "Paris", "Tokyo", "Sydney"]
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Print the result
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
## Example Output
An example output when running this task with an input of `["New York", "London", "Paris", "Tokyo", "Sydney"]`:
Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions.
### Day 1: Iconic Landmarks and Observation Decks
* **Morning:**
* **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.
* **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)
* **Afternoon:**
* **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.
* **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.
* **Evening:**
* **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.
### Day 2: Culture and History
* **Morning:**
* **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.
* **Afternoon:**
* **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.
* **Evening:**
* **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.
### Day 3: Historical and Educational
* **Morning:**
* **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.
* **Afternoon:**
* **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.
* **Evening:**
* **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.
### Additional Tips:
* **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.
* **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.
* **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.
For more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/).
## Enjoy your trip to New York City!
**Day 1: Exploring Iconic London Landmarks**
**Morning:**
1. **Buckingham Palace**
* Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.
* Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.
**Midday:**
2\. **Westminster Abbey**
* Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.
3. **Lunch at Borough Market**
* Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.
**Afternoon:**
4\. **London Eye**
* After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.
**Evening:**
5\. **The Globe Theatre**
* As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.
**Day 2: Museums and Cultural Exploration**
**Morning:**
1. **The British Museum**
* Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.
**Midday:**
2\. **Lunch near Covent Garden**
* Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.
**Afternoon:**
3\. **The Tower of London**
* After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.
**Evening:**
4\. **Tower Bridge**
* Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.
**Day 3: Leisure and Local Experiences**
**Morning:**
1. **Natural History Museum**
* Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.
**Midday:**
2\. **Lunch at South Kensington**
* Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.
**Afternoon:**
3\. **Hyde Park**
* Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.
**Evening:**
4\. **Dinner and a Show in the West End**
* End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.
**Additional Tips:**
* Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.
* London’s public transport system is efficient; consider getting an Oyster card for convenient travel.
* Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.
***
Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:
### Day 1: Embrace the Iconic Landmarks
**Morning: Eiffel Tower**
* **Time:** 9:00 AM
* **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.
* **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)
**Afternoon: Louvre Museum**
* **Time:** 1:00 PM
* **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.
* **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)
**Evening: Seine River Cruise**
* **Time:** 6:00 PM
* **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.
### Day 2: Explore Cultural and Historical Treasures
**Morning: Notre-Dame Cathedral**
* **Time:** 9:30 AM
* **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.
**Afternoon: Musée d'Orsay**
* **Time:** 1:30 PM
* **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.
**Evening: Montmartre and Sacré-Cœur**
* **Time:** 5:00 PM
* **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.
### Day 3: Discover Hidden Gems and Local Flavors
**Morning: Le Marais District**
* **Time:** 10:00 AM
* **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.
**Afternoon: Palais Garnier (Opera House)**
* **Time:** 2:00 PM
* **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.
**Evening: Moulin Rouge Show**
* **Time:** 8:00 PM
* **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.
### Additional Tips:
* **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.
* **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.
* **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.
## Enjoy your snowy adventure in Paris!
**Tokyo Itinerary**
**Day 1: Arrival and Exploration of Historical and Cultural Sites**
* **Morning:**
* **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.
* **Afternoon:**
* **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.
* **Evening:**
* **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.
**Day 2: Modern Tokyo and Unique Experiences**
* **Morning:**
* **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.
* **Afternoon:**
* **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.
* **Evening:**
* **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.
**Day 3: Relax and Explore Green Spaces**
* **Morning:**
* **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.
* **Afternoon:**
* **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.
* **Evening:**
* **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.
**Weather Considerations:**
* With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.
* Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.
**Additional Tips:**
* Always check the opening hours of attractions and book tickets in advance where necessary.
* Use Tokyo’s efficient public transport to move around easily.
* Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.
***
Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.
### Morning
**9:00 AM - Sydney Opera House**
* Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**11:00 AM - Royal Botanic Garden Sydney**
* Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.
### Afternoon
**12:30 PM - Lunch at Opera Bar**
* Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**2:00 PM - Sydney Harbour Bridge**
* After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.
**4:00 PM - The Rocks**
* Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.
### Evening
**6:00 PM - Darling Harbour**
* Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.
**8:00 PM - Dinner at a Local Restaurant**
* Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.
### Additional Suggestions
* If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.
With clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!
## Next Steps
* Try this task yourself, check out the full example, see the [trip-planning cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/03-trip-planning-assistant.ipynb).
* To learn more about the integrations used in this task, check out the [integrations](/integrations/supported-integrations) page.
**Also See:**
1. [Input Schema](./trip-planning-input)
2. [Tools Configuration](./trip-planning-tools)
3. [Workflow Steps](./trip-planning-workflow)
4. [Running the Task](./trip-planning-running)
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Tools](/concepts/tools)
# Trip Planning - Input
Source: https://docs.julep.ai/tutorials/trip-planning-input
Define the input schema for the trip planning task
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
```
This schema specifies that our task expects an array of location strings (e.g., `["New York", "London", "Paris"]`).
## Next Step
* [Tools Configuration](./trip-planning-tools)
## Related Concepts
* [Tasks](/concepts/tasks)
# Trip Planning - Running
Source: https://docs.julep.ai/tutorials/trip-planning-running
Execute the task and review the example output
## Usage
Here's how to use this task with the Julep SDK:
```python Python theme={"dark"}
from julep import Client
import time
import yaml
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
# Create the agent
agent = client.agents.create(
name="Julep Trip Planning Agent",
about="A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.",
)
# Load the task definition
with open('trip_planning_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=AGENT_ID,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={
"locations": ["New York", "London", "Paris", "Tokyo", "Sydney"]
}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
# Print the result
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```js Node.js theme={"dark"}
import { Julep } from '@julep/sdk';
import fs from 'fs';
import yaml from 'yaml';
// Initialize the client
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Julep Trip Planning Agent",
about: "A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.",
});
// Load the task definition
const taskDefinition = yaml.parse(fs.readFileSync('trip_planning_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
"locations": ["New York", "London", "Paris", "Tokyo", "Sydney"]
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Print the result
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
## Example Output
An example output when running this task with an input of `["New York", "London", "Paris", "Tokyo", "Sydney"]`:
Here's a detailed itinerary for visiting top tourist attractions in New York, considering the current weather conditions.
### Day 1: Iconic Landmarks and Observation Decks
* **Morning:**
* **Top of the Rock Observation Deck:** Start your day with a visit to the Top of the Rock Observation Deck at Rockefeller Plaza. The panoramic 360-degree views from the 70th floor are a must-see. Dress warmly as it feels like 5.2°C outside, and it’s quite windy.
* **Link for more info:** [Tripadvisor - Top of the Rock](https://www.tripadvisor.com/Attractions-g60763-Activities-New_York_City_New_York.html)
* **Afternoon:**
* **St. Patrick’s Cathedral:** Just a short walk from Rockefeller Plaza, explore the stunning architecture of St. Patrick’s Cathedral. The overcast skies will provide a dramatic backdrop for photos.
* **Fifth Avenue:** Enjoy a leisurely stroll along Fifth Avenue, visiting iconic stores and landmarks.
* **Evening:**
* **Times Square:** Experience the vibrant lights and energy of Times Square. The overcast clouds might enhance the brightness of the neon lights.
### Day 2: Culture and History
* **Morning:**
* **The Museum of Modern Art (MoMA):** Spend your morning exploring MoMA’s vast collection of modern and contemporary art. This indoor activity is perfect for a cloudy day.
* **Afternoon:**
* **Central Park:** Head to Central Park for a refreshing walk. With 100% cloud cover, it's a great day to explore the park without the harsh sun. Consider visiting the Central Park Zoo or taking a guided tour.
* **Evening:**
* **Broadway Show:** End your day with a Broadway show. It’s an ideal indoor activity to avoid the chilly weather outside. Book tickets in advance for popular shows.
### Day 3: Historical and Educational
* **Morning:**
* **Statue of Liberty and Ellis Island:** Take a ferry to visit these iconic sites. Dress warmly for the ferry ride. The cloud cover will provide a unique perspective for photos.
* **Afternoon:**
* **9/11 Memorial and Museum:** Spend your afternoon reflecting at the 9/11 Memorial and exploring the museum exhibits.
* **Evening:**
* **Brooklyn Bridge:** Walk across the Brooklyn Bridge and enjoy the city skyline. With the wind speed at 5.81 m/s, be prepared for breezy conditions.
### Additional Tips:
* **Clothing:** Wear layers to keep warm, as the temperature feels colder than it actually is.
* **Dining:** New York offers a plethora of dining options. Consider trying some local favorites like a classic New York bagel or pizza.
* **Transportation:** Utilize the subway for efficient travel across the city. Taxis and ride-sharing services are also readily available.
For more details on attractions and guided tours, you can visit [USA Guided Tours](https://usaguidedtours.com/nyc/attraction/) and [I Love NY](https://www.iloveny.com/places-to-go/new-york-city/attractions/).
## Enjoy your trip to New York City!
**Day 1: Exploring Iconic London Landmarks**
**Morning:**
1. **Buckingham Palace**
* Start your day early with a visit to Buckingham Palace. Arrive by 9:30 AM to catch the Changing of the Guard ceremony, which typically starts at 11:00 AM. Enjoy the majestic architecture and the surrounding gardens.
* Weather Tip: With the overcast clouds, it might feel chilly, so dress warmly and bring an umbrella just in case.
**Midday:**
2\. **Westminster Abbey**
* Head towards Westminster Abbey, a short walk from Buckingham Palace. This historic church has been the site of many significant events, including royal weddings and coronations. Spend about 1.5 hours exploring.
3. **Lunch at Borough Market**
* Take a tube or walk to Borough Market for a variety of food options. It's a great place to warm up with some hot street food and explore the diverse culinary offerings.
**Afternoon:**
4\. **London Eye**
* After lunch, head to the London Eye. The overcast sky might not offer the clearest views, but the experience is still worthwhile. Pre-book your tickets to avoid long lines and enjoy a 30-minute ride on this iconic Ferris wheel.
**Evening:**
5\. **The Globe Theatre**
* As the day winds down, visit Shakespeare's Globe Theatre. If there's a performance, consider attending or simply take a guided tour to learn about the history of this famous theater.
**Day 2: Museums and Cultural Exploration**
**Morning:**
1. **The British Museum**
* Start your second day at the British Museum. Spend a few hours exploring the vast collection of art and antiquities. Highlights include the Rosetta Stone and the Elgin Marbles.
**Midday:**
2\. **Lunch near Covent Garden**
* Head to Covent Garden for lunch. This area is bustling with restaurants and cafes, perfect for a cozy indoor meal.
**Afternoon:**
3\. **The Tower of London**
* After lunch, make your way to the Tower of London. Delve into England's rich history and see the Crown Jewels. Allocate about 2-3 hours for this visit.
**Evening:**
4\. **Tower Bridge**
* Conclude your day with a walk across Tower Bridge. The views of the Thames River and the cityscape are beautiful, even on a cloudy evening.
**Day 3: Leisure and Local Experiences**
**Morning:**
1. **Natural History Museum**
* Begin your last day at the Natural History Museum. It's a family-friendly museum with fascinating exhibits, including dinosaur skeletons and a model of a blue whale.
**Midday:**
2\. **Lunch at South Kensington**
* Enjoy a relaxed lunch in South Kensington. There are plenty of options, from casual cafes to high-end dining.
**Afternoon:**
3\. **Hyde Park**
* Spend your afternoon strolling through Hyde Park. Visit the Serpentine Galleries if you're interested in contemporary art. The park's natural beauty is a peaceful retreat amidst the city hustle.
**Evening:**
4\. **Dinner and a Show in the West End**
* End your London adventure with a memorable dinner followed by a theater show in the West End. Book your tickets in advance for popular shows.
**Additional Tips:**
* Always check the attraction websites for any specific COVID-19 guidelines or changes in operating hours.
* London’s public transport system is efficient; consider getting an Oyster card for convenient travel.
* Don't forget to dress in layers to adapt to the chilly weather, and always have your camera ready to capture memories.
***
Here’s a detailed itinerary for your visit to Paris, considering the current snowy weather conditions and top attractions:
### Day 1: Embrace the Iconic Landmarks
**Morning: Eiffel Tower**
* **Time:** 9:00 AM
* **Details:** Begin your day with a visit to the Eiffel Tower. Even in the snow, the tower offers stunning views of Paris. Dress warmly and enjoy hot chocolate from nearby cafes.
* **Link for more info:** [Tripadvisor - Things to Do in Paris](https://www.tripadvisor.com/Attractions-g187147-Activities-Paris_Ile_de_France.html)
**Afternoon: Louvre Museum**
* **Time:** 1:00 PM
* **Details:** Spend your afternoon indoors at the Louvre Museum. With its vast collection of art and history, it's a perfect way to escape the cold. Consider taking a guided tour to make the most of your visit.
* **Link for more info:** [U.S. News Travel - Things To Do](https://travel.usnews.com/Paris_France/Things_To_Do/)
**Evening: Seine River Cruise**
* **Time:** 6:00 PM
* **Details:** End your day with a magical Seine River cruise. The snow adds a picturesque touch to the illuminated landmarks. Ensure to book a heated cruise for comfort.
### Day 2: Explore Cultural and Historical Treasures
**Morning: Notre-Dame Cathedral**
* **Time:** 9:30 AM
* **Details:** Visit the iconic Notre-Dame Cathedral. Although some areas may be under restoration, its architecture and history are worth experiencing. Warm clothing is essential as the interior can be chilly.
**Afternoon: Musée d'Orsay**
* **Time:** 1:30 PM
* **Details:** Head to the Musée d'Orsay, renowned for its Impressionist masterpieces. This indoor activity is ideal for escaping the cold and enjoying world-class art.
**Evening: Montmartre and Sacré-Cœur**
* **Time:** 5:00 PM
* **Details:** Wander through the charming streets of Montmartre and visit the Sacré-Cœur Basilica. The view of Paris in the snow is breathtaking. Enjoy a cozy dinner at a local bistro in Montmartre.
### Day 3: Discover Hidden Gems and Local Flavors
**Morning: Le Marais District**
* **Time:** 10:00 AM
* **Details:** Explore Le Marais, known for its vibrant street art, boutiques, and cafes. Enjoy a leisurely breakfast and shop for unique souvenirs.
**Afternoon: Palais Garnier (Opera House)**
* **Time:** 2:00 PM
* **Details:** Tour the opulent Palais Garnier. Its stunning interiors are a must-see, especially when it's snowy outside.
**Evening: Moulin Rouge Show**
* **Time:** 8:00 PM
* **Details:** Conclude your trip with a classic Parisian experience at the Moulin Rouge. Book in advance to secure a good seat and enjoy the legendary cabaret performance.
### Additional Tips:
* **Weather Preparation:** Wear layers, waterproof boots, and carry an umbrella. The snow and cold wind can be intense.
* **Dining:** Indulge in warm, hearty French cuisine at local cafes and restaurants. Try dishes like French onion soup, coq au vin, and tarte Tatin.
* **Transport:** Use public transportation to avoid the snowy streets, and consider purchasing a Paris Visite pass for unlimited travel.
## Enjoy your snowy adventure in Paris!
**Tokyo Itinerary**
**Day 1: Arrival and Exploration of Historical and Cultural Sites**
* **Morning:**
* **Asakusa District**: Begin your day with a visit to the historic Asakusa district. Explore the iconic Senso-ji Temple, Tokyo's oldest temple. Enjoy the traditional market streets like Nakamise Street for some shopping and snacks.
* **Afternoon:**
* **Tokyo National Museum**: Head to Ueno Park and visit the Tokyo National Museum. Discover Japan’s extensive collection of art and antiquities. This is a great spot to dive into Japanese history and culture.
* **Evening:**
* **Dinner in Ueno**: Explore the local dining options around Ueno and enjoy a traditional Japanese dinner.
**Day 2: Modern Tokyo and Unique Experiences**
* **Morning:**
* **Ghibli Museum**: Start your day with a magical visit to the Ghibli Museum in Mitaka. Perfect for fans of Studio Ghibli's animated films, this museum offers a whimsical look into the creative world of Hayao Miyazaki.
* **Afternoon:**
* **Shibuya and Harajuku**: Head towards the bustling areas of Shibuya and Harajuku. Witness the famous Shibuya Crossing and explore the trendy shops of Harajuku, especially Takeshita Street.
* **Evening:**
* **Golden Gai**: Conclude your day in the vibrant Golden Gai district. This area is renowned for its narrow alleys filled with small bars and eateries. Experience the unique nightlife of Tokyo here.
**Day 3: Relax and Explore Green Spaces**
* **Morning:**
* **Shinjuku Gyoen National Garden**: Spend a peaceful morning strolling through the beautiful Shinjuku Gyoen, one of Tokyo's largest and most beautiful parks. It's a perfect spot for relaxation and enjoying nature.
* **Afternoon:**
* **Meiji Shrine**: Visit the Meiji Shrine, located in a forested area near Harajuku and Shibuya. It's a serene place to learn about Shinto traditions and enjoy the tranquil setting.
* **Evening:**
* **Tokyo Tower or Skytree**: End your trip with a visit to either Tokyo Tower or Tokyo Skytree for a panoramic view of the city. It's an unforgettable way to see Tokyo illuminated at night.
**Weather Considerations:**
* With the current weather of few clouds and a mild temperature around 10.32°C, it is advisable to wear layers and carry a light jacket for comfort during outdoor activities.
* Humidity is high (92%), so be prepared for a slightly damp feeling and consider moisture-wicking clothing.
**Additional Tips:**
* Always check the opening hours of attractions and book tickets in advance where necessary.
* Use Tokyo’s efficient public transport to move around easily.
* Consider visiting the websites linked in the attraction descriptions for more detailed information and current updates.
***
Here's a detailed itinerary for exploring some of Sydney's top tourist attractions with the current weather conditions in mind. With clear skies and pleasant temperatures, it's a perfect day to explore the outdoors and enjoy what Sydney has to offer.
### Morning
**9:00 AM - Sydney Opera House**
* Begin your day with a visit to the iconic Sydney Opera House. Take a guided tour to learn about its history and architecture. Tours are available in multiple languages.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**11:00 AM - Royal Botanic Garden Sydney**
* Just a short walk from the Opera House, enjoy a leisurely stroll through the Royal Botanic Garden. The clear skies will offer beautiful views of the diverse plant life and the Sydney Harbour.
### Afternoon
**12:30 PM - Lunch at Opera Bar**
* Head back to Opera Bar for lunch. Enjoy a refreshing cocktail with stunning views of the Sydney Harbour Bridge and the waterfront.
* **Link:** [Top attractions in Sydney | Sydney.com](https://www.sydney.com/things-to-do/attractions)
**2:00 PM - Sydney Harbour Bridge**
* After lunch, take a scenic walk across the Sydney Harbour Bridge. If you're up for it, consider the BridgeClimb for breathtaking panoramic views of the city.
**4:00 PM - The Rocks**
* Explore The Rocks, one of Sydney's most historic areas. Wander through the cobbled streets, visit the local markets, and perhaps enjoy a cup of coffee at a nearby café.
### Evening
**6:00 PM - Darling Harbour**
* Make your way to Darling Harbour for the evening. Here you can visit attractions such as the SEA LIFE Sydney Aquarium or simply enjoy the lively atmosphere by the waterfront.
**8:00 PM - Dinner at a Local Restaurant**
* Conclude your day with dinner at one of Darling Harbour's many restaurants. Choose from a variety of cuisines while enjoying the vibrant night scene.
### Additional Suggestions
* If you're interested in more unique experiences, consider visiting some of the attractions listed on [Time Out Sydney](https://www.timeout.com/sydney/attractions/tourist-attractions-that-dont-suck), which includes thrilling adventures and scenic tours.
With clear skies and mild temperatures, this itinerary offers a balanced mix of cultural, historical, and scenic experiences. Enjoy your visit to Sydney!
## Next Steps
* Try this task yourself, check out the full example, see the [trip-planning cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/03-trip-planning-assistant.ipynb).
* To learn more about the integrations used in this task, check out the [integrations](/integrations/supported-integrations) page.
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Executions](/concepts/execution)
# Trip Planning - Tools
Source: https://docs.julep.ai/tutorials/trip-planning-tools
Configure tools for fetching weather and attraction data
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: YOUR_OPENWEATHERMAP_API_KEY
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
```
We're using two integrations:
* The `weather` integration to fetch current weather conditions
* The `brave` search integration to find tourist attractions
## Next Step
* [Workflow Steps](./trip-planning-workflow)
## Related Concepts
* [Tools](/concepts/tools)
# Trip Planning - Workflow
Source: https://docs.julep.ai/tutorials/trip-planning-workflow
Learn the step-by-step workflow for generating itineraries
### 3. Main Workflow Steps
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
```
When used inside a `map` or a `foreach` step, the `_` variable is a reference to the current value in the iteration.
For example:
```yaml theme={"dark"}
- over: $ ["Paris", "London"]
map:
tool: weather
arguments:
location: $ _ # _ will be "Paris", then "London"
```
This step:
* Iterates over each location in the input array
* Calls the weather API for each location
```yaml theme={"dark"}
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
```
This step:
* Iterates over the locations again
* Searches for tourist attractions in each location
* Constructs a search query by concatenating "tourist attractions in " with the location
```yaml theme={"dark"}
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
```
This step:
* Combines the data from previous steps into tuples
* Each tuple contains: (location, weather\_data, attractions\_data)
* Uses Python's `zip` function to align the data
```yaml theme={"dark"}
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
```
This step:
* Processes up to 3 locations in parallel
* For each location tuple:
* Sends a prompt to the AI model
* Includes location, weather, and attraction data
* Generates a personalized itinerary
```yaml theme={"dark"}
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
```
This step:
* Combines all itineraries into a single output
* Separates each itinerary with a divider
```yaml YAML theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Trip Planning Task
description: A Julep agent that can generate a detailed itinerary for visiting tourist attractions in some locations, considering the current weather conditions.
########################################################
################### INPUT SCHEMA #######################
########################################################
input_schema:
type: object
properties:
locations:
type: array
items:
type: string
description: The locations to search for.
########################################################
################### TOOLS ##############################
########################################################
tools:
- name: wikipedia
type: integration
integration:
provider: wikipedia
- name: weather
type: integration
integration:
provider: weather
setup:
openweathermap_api_key: "YOUR_OPENWEATHERMAP_API_KEY"
- name: internet_search
type: integration
integration:
provider: brave
setup:
brave_api_key: "YOUR_BRAVE_API_KEY"
########################################################
################### MAIN WORKFLOW ######################
########################################################
main:
- over: $ steps[0].input.locations
map:
tool: weather
arguments:
location: $ _
- over: $ steps[0].input.locations
map:
tool: internet_search
arguments:
query: $ 'tourist attractions in ' + _
# Zip locations, weather, and attractions into a list of tuples [(location, weather, attractions)]
- evaluate:
zipped: |-
$ list(
zip(
steps[0].input.locations,
[output['result'] for output in steps[0].output],
steps[1].output
)
)
- over: $ _['zipped']
parallelism: 3
# Inside the map step, each `_` represents the current element in the list
# which is a tuple of (location, weather, attractions)
map:
prompt:
- role: system
content: >-
$ f'''You are {agent.name}. Your task is to create a detailed itinerary
for visiting tourist attractions in some locations.
The user will give you the following information for each location:
- The location
- The current weather condition
- The top tourist attractions'''
- role: user
content: >-
$ f'''Location: "{_[0]}"
Weather: "{_[1]}"
Attractions: "{_[2]}"'''
unwrap: true
- evaluate:
final_plan: |-
$ '\\n---------------\\n'.join(activity for activity in _)
```
## Next Step
* [Running the Task](./trip-planning-running)
## Related Concepts
* [Tasks](/concepts/tasks)
* [Types of Task Steps](/advanced/types-of-task-steps)
# Video Processing
Source: https://docs.julep.ai/tutorials/video-processing
Learn how to process and analyze videos using Julep
## Overview
This tutorial demonstrates how to:
* Upload and process videos using Cloudinary integration
* Extract and analyze video content
* Add overlays and transformations
* Process video subtitles and speaker information
## Task Structure
Let's break down the task into its core components:
### 1. Input Schema
First, we define what inputs our task expects:
```yaml theme={"dark"}
input_schema:
type: object
properties:
upload_file:
type: string
description: The url of the file to upload
public_id:
type: string
description: The public id of the file to upload
transformation_prompt:
type: string
description: The prompt for the transformations to apply to the file
subtitle_vtt:
type: string
description: The vtt file content to add subtitles to the video
```
This schema specifies that our task expects:
* A video file URL
* A public ID for the video
* A transformation prompt describing desired changes
* VTT subtitle content (optional)
### 2. Tools Configuration
Next, we define the external tools our task will use:
```yaml theme={"dark"}
- name: cloudinary_upload
type: integration
integration:
provider: cloudinary
method: media_upload
setup:
cloudinary_api_key: YOUR_CLOUDINARY_API_KEY
cloudinary_api_secret: YOUR_CLOUDINARY_API_SECRET
cloudinary_cloud_name: YOUR_CLOUDINARY_CLOUD_NAME
- name: cloudinary_edit
type: integration
integration:
provider: cloudinary
method: media_edit
- name: ffmpeg_edit
type: integration
integration:
provider: ffmpeg
```
We're using three main integrations:
* Cloudinary for video uploads and transformations
* FFmpeg for additional video processing capabilities
### 3. Main Workflow Steps
```yaml theme={"dark"}
- tool: cloudinary_upload
arguments:
file: $ steps[0].input.video_url
public_id: $ steps[0].input.public_id
upload_params:
resource_type: video
```
The `steps[0].input` variable refers to the initial input object passed to the task. It's used to access the input parameters defined in the input schema.
This step:
* Takes the input video URL
* Uploads it to Cloudinary
* Specifies the resource type as video
```yaml theme={"dark"}
- tool: cloudinary_upload
arguments:
file: $ steps[0].input.upload_file
public_id: $ steps[0].input.public_id
upload_params:
resource_type: video
transformation:
- start_offset: 0
end_offset: 30
```
This step:
* Creates a 30-second preview of the video
* Useful for quick analysis and processing
```yaml theme={"dark"}
- prompt:
- role: user
content:
- type: image_url
image_url:
url: trimmed_video_url
- type: text
text: |-
Which speakers are speaking in the video? And where does each of them sit?
```
This step:
* Analyzes the video content
* Identifies speakers and their positions
* Uses VTT subtitles for additional context
```yaml theme={"dark"}
- evaluate:
speakers_transformations: |-
$ [
transform
for speaker in _.speakers_json
for transform in [
{
"overlay": {"font_family": "Arial", "font_size": 32, "text": speaker.speaker},
"color": "white"
},
{
"duration": 5,
"flags": "layer_apply",
"gravity": "south_east" if speaker.position == "right" else "south_west",
"start_offset": speaker.timestamps[0].start,
"y": 80,
"x": 80
}
]
]
```
This step:
* Creates transformations for each speaker
* Adds speaker labels with proper positioning
* Sets timing for each overlay
```yaml theme={"dark"}
- tool: cloudinary_upload
arguments:
file: $ steps[0].input.upload_file
public_id: $ steps[0].input.public_id
upload_params:
resource_type: video
transformation: _.speakers_transformations
```
This step:
* Uses the Cloudinary upload tool to apply the generated transformations
* Processes the video with speaker labels and positioning
* Returns a URL to the transformed video with all overlays applied
````yaml YAML [expandable] theme={"dark"}
# yaml-language-server: $schema=https://raw.githubusercontent.com/julep-ai/julep/refs/heads/dev/src/schemas/create_task_request.json
name: Julep Video Processing Task
description: A Julep agent that can process and analyze videos using Cloudinary
########################################################
################### INPUT SCHEMA #######################
########################################################
input_schema:
type: object
properties:
video_url:
type: string
description: The url of the file to upload
public_id:
type: string
description: The public id of the file to upload
transformation_prompt:
type: string
description: The prompt for the transformations to apply to the file
########################################################
################### TOOLS ##############################
########################################################
tools:
- name: cloudinary_upload
type: integration
integration:
provider: cloudinary
method: media_upload
setup:
cloudinary_api_key: "YOUR_CLOUDINARY_API_KEY"
cloudinary_api_secret: "YOUR_CLOUDINARY_API_SECRET"
cloudinary_cloud_name: "YOUR_CLOUDINARY_CLOUD_NAME"
########################################################
################### MAIN WORKFLOW ######################
########################################################
main:
# Step #0 - Upload the video to cloudinary
- tool: cloudinary_upload
arguments:
file: $ steps[0].input.video_url
public_id: $ steps[0].input.public_id
upload_params:
resource_type: video
# Step #1 - Analyze the video content with a prompt
- prompt:
- role: user
content:
- type: text
text: |-
You are a Cloudinary expert. You are given a medial url. it might be an image or a video.
You need to come up with a json of transformations to apply to the given media.
Overall the json could have multiple transformation json objects.
Each transformation json object can have the multiple key value pairs.
Each key value pair should have the key as the transformation name like "aspect_ratio", "crop", "width" etc and the value as the transformation parameter value.
Given below is an example of a transformation json list. Don't provide explanations and/or comments in the json.
``json
[
{{
"aspect_ratio": "1.0",
"width": 250,
}},
{{
"fetch_format": "auto"
}},
{{
"overlay":
{{
"url": ""
}}
}},
{{
"flags": "layer_apply"
}}
]
``
- type: image_url
image_url:
url: $ _.url
- type: text
text: |-
$ f'''Hey, check the video above, I need to apply the following transformations using cloudinary.
{steps[0].input.transformation_prompt}'''
unwrap: true
settings:
model: gemini/gemini-1.5-pro
# Step #2 - Extract the json from the model's response
- evaluate:
model_transformation: >-
$ load_json(
_[_.find("```json")+7:][:_[_.find("```json")+7:].find("```")])
# Step #3 - Upload the video to cloudinary
- tool: cloudinary_upload
arguments:
file: $ steps[0].input.video_url
public_id: $ steps[0].input.public_id
upload_params:
transformation: $ _.model_transformation
resource_type: video
# Step #4 - Evaluate the transformed video url
- evaluate:
transformed_video_url: $ _.url
````
## Usage
Here's how to use this task with the Julep SDK:
```python Python [expandable] theme={"dark"}
import time
import yaml
from julep import Client
# Initialize the client
client = Client(api_key=JULEP_API_KEY)
transformation_prompt = """
1- I want to add an overlay an the following image to the video, and apply a layer apply flag also. Here's the image url:
https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png
2- I also want you to to blur the video, and add a fade in and fade out effect to the video with a duration of 3 seconds each.
"""
# Create the agent
agent = client.agents.create(
name="Julep Video Processing Agent",
about="A Julep agent that can process and analyze videos using Cloudinary and FFmpeg.",
)
# Load the task definition
with open('video_processing_task.yaml', 'r') as file:
task_definition = yaml.safe_load(file)
# Create the task
task = client.tasks.create(
agent_id=agent.id,
**task_definition
)
# Create the execution
execution = client.executions.create(
task_id=task.id,
input={
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4",
"public_id": "video_test",
"transformation_prompt": transformation_prompt,
}
)
# Wait for the execution to complete
while (result := client.executions.get(execution.id)).status not in ['succeeded', 'failed']:
print(result.status)
time.sleep(1)
# Print the result
if result.status == "succeeded":
print(result.output)
else:
print(f"Error: {result.error}")
```
```js Node.js [expandable] theme={"dark"}
import { Julep } from '@julep/sdk';
import yaml from 'yaml';
import fs from 'fs';
const transformation_prompt = `
1- I want to add an overlay an the following image to the video, and apply a layer apply flag also. Here's the image url:
https://res.cloudinary.com/demo/image/upload/logos/cloudinary_icon_white.png
2- I also want you to to blur the video, and add a fade in and fade out effect to the video with a duration of 3 seconds each.
`;
const client = new Julep({
apiKey: 'your_julep_api_key'
});
// Create the agent
const agent = await client.agents.create({
name: "Julep Video Processing Agent",
about: "A Julep agent that can process and analyze videos using Cloudinary and FFmpeg.",
});
// Load the task definition
const taskDefinition = yaml.parse(fs.readFileSync('video_processing_task.yaml', 'utf8'));
// Create the task
const task = await client.tasks.create(
agent.id,
taskDefinition
);
// Create the execution
const execution = await client.executions.create(
task.id,
{
input: {
"video_url": "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerMeltdowns.mp4",
"public_id": "video_test",
"transformation_prompt": transformation_prompt,
}
}
);
// Wait for the execution to complete
let result;
while (true) {
result = await client.executions.get(execution.id);
if (result.status === 'succeeded' || result.status === 'failed') break;
console.log(result.status);
await new Promise(resolve => setTimeout(resolve, 1000));
}
// Print the result
if (result.status === 'succeeded') {
console.log(result.output);
} else {
console.error(`Error: ${result.error}`);
}
```
## Example Output
This is an example output when the task is run over the sample video input.
## Next Steps
* Try this task yourself, check out the full example, see the [video-processing-with-natural-language cookbook](https://github.com/julep-ai/julep/blob/main/cookbooks/advanced/05-video-processing-with-natural-language.ipynb).
* To learn more about the integrations used in this task, check out the [integrations](/integrations/supported-integrations) page.
## Related Concepts
* [Agents](/concepts/agents)
* [Tasks](/concepts/tasks)
* [Tools](/concepts/tools)