Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.git
.omx
.pytest_cache
.venv
.env
.env.*
!.env.example
__pycache__
*.pyc
*.pyo
*.pyd
*.swp
*.swo
*~
._*
Thumbs.db
.DS_Store
AWSCLIV2.pkg
docker_container
grpc_stubs
stubs
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
pull_request:
branches: [ main ]
workflow_dispatch:


permissions:
contents: read


jobs:
test:
runs-on: ubuntu-latest
Comment thread
coderabbitai[bot] marked this conversation as resolved.

steps:
- name: Checkout code
uses: actions/checkout@v4
Comment on lines +18 to +19

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Disable checkout credential persistence.

Set persist-credentials: false on actions/checkout; otherwise the token remains in the local Git configuration and can be exposed by later commands or generated artifacts.

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 13-14: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 13 - 14, Update the “Checkout code”
step using actions/checkout@v4 to set persist-credentials to false, preventing
the checkout token from being retained in local Git configuration.

Source: Linters/SAST tools


- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true

- name: "Run Ruff: Lint"
run: uvx ruff check .

- name: "Run Ruff: Format Check"
run: uvx ruff format --check .

- name: "Install dependencies for Ty"
run: |
uv venv
uv pip install -e .

- name: "Run Ty: Type Check"
run: uvx ty check

# If you want to run this locally, install act and run "act pull_request"
# For fixing Ruff lint errors: uvx ruff check --fix .
# For fixing Ruff formatting errors: uvx ruff format .
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ env/
# OS files
.DS_Store
Thumbs.db
._*

# Generated stubs
stubs/
Expand All @@ -32,3 +33,12 @@ docker_container/

# Logs
*.log

# Local env / machine artifacts
.env
.env.*
!.env.example
AWSCLIV2.pkg
.python-version

sandbox/
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ git clone https://github.com/your-repo/ventis.git
cd ventis
pip install -e .
```
Note: Installation of ventis only needs to be done on the machine where you are running the deploy command. It does not need to be installed on the remote hosts where the agents are deployed. Ventis will automatically push code and environment to the remote hosts.
Note: Installation of ventis only needs to be done on the machine where you are running the deploy command. It does not need to be installed on the remote hosts where the agents are deployed. Ventis runs the built container images on the target hosts; for remote EC2 deployments, make sure the image is already available on the host.

### 2. Prerequisites

Expand Down Expand Up @@ -68,7 +68,7 @@ cp -r ../examples/* ./
## Deployment Guide

#### Step 1: Configure the Global Controller
Edit `examples/config/global_controller.yaml` to list the agents you want to deploy, their hosts, ports, and resource limits.
Edit `config/global_controller.yaml` in your project directory to list the agents you want to deploy, their `provider`, `replicas`, and resource limits.

#### Step 2: Build the project
```bash
Expand All @@ -89,10 +89,10 @@ Upon running the deploy command, ventis automatically generates a REST API endpo
Users can send requests to this endpoint to trigger the workflow. For this example, workflow to send a request -

```bash
curl -X POST http://localhost:8080/finance_workflow/run \
curl -X POST http://localhost:8080/main \
-H "Content-Type: application/json" \
-d '{
"query": "What is the current stock price of Apple?"
"ticker": "AAPL"
}'
```
The request is asynchronous. To get the result, you use the following URL-
Expand All @@ -105,7 +105,7 @@ curl http://localhost:8080/status/<request_id>
Remove all generated stub and gRPC files:

```bash
make clean
ventis clean
```

### Harnessing the power of Ventis
Expand Down
9 changes: 6 additions & 3 deletions examples/agents/finance_agent.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from vllm_agent_stub import VllmAgentStub


# Example of a simple finance agent
class FinanceAgent(object):
def __init__(self):
Expand All @@ -19,15 +21,16 @@ def get_company_name(self, ticker: str) -> str:
def run(self, query: str) -> str:
# company = self.get_company_name(query)
# price = self.get_stock_price(company)

prompt = f"The company is {query} and the stock price is . Please write a short, professional response."

# Call the VLLM agent remotely and wait for the result
# .value() blocks until the future completes via Redis
response = self.vllm.generate(prompt).value()
# print(response.value())
return response


if __name__ == "__main__":
agent = FinanceAgent()
print(agent.run("What is the stock price of Apple?"))
print(agent.run("What is the stock price of Apple?"))
3 changes: 2 additions & 1 deletion examples/agents/market_agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Market Research Agent


class MarketResearchAgent(object):
def __init__(self):
self.tools = [self.get_market_trend, self.get_sector_analysis]
Expand All @@ -19,4 +20,4 @@ def get_competitor_list(self, company: str) -> list:
def run(self, query: str) -> str:
trend = self.get_market_trend(query)
analysis = self.get_sector_analysis(query)
return f"{analysis} Trend: {trend['trend']}"
return f"{analysis} Trend: {trend['trend']}"
4 changes: 3 additions & 1 deletion examples/agents/vllm_agent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Simple VLLM Agent Example


class VllmAgent(object):
def __init__(self):
self.tools = [self.generate]
Expand All @@ -10,11 +11,12 @@ def __init__(self):
def generate(self, prompt: str) -> str:
"""Generates a response using an LLM model based on the given prompt."""
print(f"VllmAgent: Received prompt: '{prompt}'")

# Simulated LLM generation
synthetic_response = f"This is an LLM generated response to: '{prompt}'"
return synthetic_response


if __name__ == "__main__":
agent = VllmAgent()
print(agent.generate("What is the stock price?"))
31 changes: 31 additions & 0 deletions examples/config/global_controller.ec2_smoke.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# EC2 variant of the main example config:
# - exactly 2 agent instances
# - generic agent image + bind-mounted project code

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the smoke-config deployment assumption.

The EC2 launcher runs ventis-marketresearchagent with no bind mount and does not use entrypoint; this sample requires a prebuilt image containing the agent code, not a generic image plus mounted project source.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/config/global_controller.ec2_smoke.yaml` at line 3, Update the smoke
configuration comment to state that the EC2 launcher uses a prebuilt image
containing the agent code, without a bind mount or entrypoint override; remove
the incorrect “generic agent image + bind-mounted project code” assumption.

# - t2.nano for lowest-cost MVP validation

agents:
- name: MarketResearchAgent
replicas: 2
redis_port: 6379
instance_type: t2.nano
resources:
cpu: 1
memory: 256
entrypoint: agents/market_agent.py
provider: EC2


poll_interval: 5

redis:
host: localhost
port: 6379
db: 0

ec2:
region: us-east-1
ami_id: ami-0123456789abcdef0
subnet_id: subnet-0123456789abcdef0
security_group_ids:
- sg-0123456789abcdef0
ssh_user: ubuntu
63 changes: 28 additions & 35 deletions examples/config/global_controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,63 @@

agents:
- name: FinanceAgent
# Replicas can be placed on different hosts with explicit host/port.
# Alternatively, use `replicas: 3` as shorthand to launch 3 instances
# on the same host with sequential ports starting from `port`.
replicas:
- host: localhost
port: 8051
- host: localhost
port: 8052
redis_port: 6379 # Redis port on this node
# user: sagarwal # SSH user for remote hosts (omit for localhost)
# Stateful agents get session affinity: all calls within a single
# request_id are routed to the same instance.
replicas: 2
redis_port: 6379 # Redis port on this node
stateful: true
# Resource limits per instance
resources:
cpu: 1 # Number of CPU cores
memory: 512 # Memory in MB
# Path to the agent entrypoint script
cpu: 1
memory: 512
entrypoint: agents/finance_agent.py
provider: local

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does provider do here ? How will the values change when it's not local ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In runtime_manager.py, when the instance gets created in ensure_instances(), the logic to create the instance for the agent would be different if the provider was local vs EC2.


- name: MarketResearchAgent
host: localhost
port: 8053
redis_port: 6379
# user: sagarwal
replicas: 1
redis_port: 6379
resources:
cpu: 1
memory: 512
entrypoint: agents/market_agent.py
provider: local

- name: VllmAgent
host: localhost
port: 8054
redis_port: 6379
replicas: 1
redis_port: 6379
resources:
cpu: 2
memory: 2048
entrypoint: agents/vllm_agent.py
provider: local

- name: Workflow
host: localhost
port: 8050 # LC gRPC port (exposed to host)
replicas: 1
type: workflow
api_port: 8080 # Flask REST API port (exposed to host)
redis_port: 6379
replicas: 1
workflow_file: workflows/example_workflow.py
provider: local

# Polling interval in seconds
poll_interval: 5

# Redis connection
redis:
host: localhost
port: 6379
db: 0

# Docker image registry (optional).
# If set, `ventis deploy` will push images to this registry locally and
# pull them on remote nodes before starting containers.
# If omitted, images are shipped to remote nodes via `docker save | ssh ... docker load`.
# EC2 defaults for `provider: EC2` replicas.
# Security group must allow inbound TCP 22, 50051, and 6379 from the
# global controller host's source IP.
#
# ec2:
# region: us-east-1
# ami_id: ami-0123456789abcdef0
# subnet_id: subnet-0123456789abcdef0
# security_group_ids:
# - sg-0123456789abcdef0
# ssh_user: ubuntu


# Docker image registry (legacy; no longer used).
# Remote nodes are expected to already have the image before `ventis deploy`.
#
# registry:
# url: myregistry.example.com:5000 # Registry host:port
# user: sagarwal # (optional) SSH user used when pulling on remote nodes
# url: myregistry.example.com:5000
# user: sagarwal
25 changes: 25 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.0"
description = "Distributed agent orchestration framework"
requires-python = ">=3.10"
dependencies = [
"boto3",
"grpcio",
"grpcio-tools",
"redis",
Expand All @@ -26,3 +27,27 @@ ventis = [
"templates/**/*",
"controller/proto/*.proto",
]



[tool.ty.environment]
python = ".venv"

[tool.ty.src]
include = ["ventis"]
exclude = [
"ventis/templates/**",
"ventis/stub_generator.py",
]

[tool.ty.analysis]
allowed-unresolved-imports = [
"local_controler_pb2",
"local_controler_pb2_grpc",
"local_controller_frontend",
"redis_client",
"ventis_context",
"deploy",
"*_stub",
"*_agent_stub",
]
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
boto3
grpcio
grpcio-tools
redis
Expand Down
Binary file added session-manager-plugin.pkg
Binary file not shown.
5 changes: 3 additions & 2 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ This directory contains an automated end-to-end testing suite for Ventis. It is

## 1. Automated Test Runner (`run_tests.sh`)
This script automates the entire testing lifecycle by interacting with the `ventis` CLI:
0. Runs a small pytest suite from this `tests/` directory.
1. Scaffolds a new temporary project using `ventis new-project`.
2. Compiles the project using `ventis build`.
3. Launches the project using `ventis deploy` in the background.
4. Waits for the GlobalController and all agent sidecars to become healthy.
4. Waits for the deployed workflow endpoint to become reachable, then gives the agents a few extra seconds to register.
5. Runs the Python integration and performance scripts.
6. **Cleanup:** Automatically terminates the deployment and cleans up the temporary directory upon success or failure.

Expand All @@ -19,7 +20,7 @@ To run the complete suite:
## 2. Functional Integration Validation (`test_integration.py`)
Verifies that Ventis correctly passes data and dependencies between chained agents.
- Dispatches a single query to the deployed `/main` endpoint.
- Polls the `/status` endpoint until completion.
- Polls the `/status/<request_id>` endpoint until completion.
- Validates the output payload structure and ensures that data successfully flowed through `FinanceAgent`, `MarketResearchAgent`, and `VllmAgent`.

To run manually against an already-deployed Ventis instance:
Expand Down
Loading
Loading