Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe400fb
feat(examples): add live-search-bot implementation
CarlKho-Minerva Feb 5, 2025
ea26ee2
bounty (1/2)
CarlKho-Minerva Feb 15, 2025
5c14edf
feat(weather): implement weather fetching functionality with API inte…
CarlKho-Minerva Feb 15, 2025
be78b7b
feat(marketplace): implement GPU marketplace data fetching functionality
CarlKho-Minerva Feb 15, 2025
76517d1
feat(gpu-marketplace-bot): enhance system instructions for GPU availa…
CarlKho-Minerva Feb 15, 2025
36ee0a9
bug: prices should return in cents.
CarlKho-Minerva Feb 15, 2025
94ba56a
feat(gpu-marketplace-bot): update README to reflect new GPU marketpla…
CarlKho-Minerva Feb 15, 2025
7047438
REVERT feat(gpu-marketplace-bot): refactor fetch_marketplace_data for…
CarlKho-Minerva Feb 15, 2025
e270bd6
Merge branch 'HyperbolicLabs:master' into carl-feat/interruptable-mul…
CarlKho-Minerva Apr 27, 2025
8d8fe77
Add region mapping and update price formatting in marketplace data fetch
CarlKho-Minerva Apr 27, 2025
d68fbe7
Add memory formatting function and update GPU data structure in marke…
CarlKho-Minerva Apr 27, 2025
cdb292c
Enhance system instructions for GPU Marketplace assistant to include …
CarlKho-Minerva Apr 27, 2025
f898d56
Refine GPU instance listing and user guidance in marketplace data fetch
CarlKho-Minerva Apr 27, 2025
56c1b42
Add validator notebook for GPU marketplace data processing and output
CarlKho-Minerva Apr 27, 2025
6254423
Add configuration and marketplace data fetching functionality for GPU…
CarlKho-Minerva Apr 27, 2025
1df419a
Add tool declarations and registration for GPU marketplace functions
CarlKho-Minerva Apr 27, 2025
8f9b67f
IMPORTANT: inference_on_context_initialization=True
CarlKho-Minerva Apr 27, 2025
72a08b5
Cloud Run. Add Dockerfile and health check server; update requirement…
CarlKho-Minerva Apr 27, 2025
2959ce6
Refactor WebSocket event handler and improve error handling in main f…
CarlKho-Minerva Jun 14, 2025
9937bfd
Refactor Dockerfile for clarity and consistency in comments and forma…
CarlKho-Minerva Jun 14, 2025
08de21f
Add launcher script and update Dockerfile to use it as the entry point
CarlKho-Minerva Jun 14, 2025
1b417e9
Add missing import for os module in launcher.py
CarlKho-Minerva Jun 14, 2025
a7fb35e
Refactor bot launch process to use subprocess for better isolation an…
CarlKho-Minerva Jun 14, 2025
45e16be
Refactor bot launcher to manage state and process lifecycle; remove h…
CarlKho-Minerva Jun 14, 2025
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
8 changes: 8 additions & 0 deletions multimodal-gpu_marketplace-bot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Might need to replace w Hyperbolic's API Keys / Credentials

# Daily.co API credentials
DAILY_API_KEY=your_daily_api_key_here
DAILY_SAMPLE_ROOM_URL=your_daily_room_url_here # Optional: URL of an existing Daily room

# Google Gemini API credentials
GOOGLE_API_KEY=your_gemini_api_key_here
31 changes: 31 additions & 0 deletions multimodal-gpu_marketplace-bot/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Use an official Python runtime as a parent image
FROM python:3.12-slim

# Set environment variables to prevent Python from writing .pyc files
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# Set the working directory in the container
WORKDIR /app

# Install system dependencies needed for audio processing (for pipecat's Silero VAD)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*

# Copy the requirements file into the container
COPY requirements.txt .

# Install any needed packages specified in requirements.txt
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt

# Copy the rest of your application code into the container
COPY . .

# Expose the port your healthcheck server runs on
EXPOSE 8080

# The command to run your application
CMD ["python", "launcher.py"]
77 changes: 77 additions & 0 deletions multimodal-gpu_marketplace-bot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# GPU Marketplace Voice Assistant Bot

An interactive voice assistant bot that helps users explore and find GPU instances on the Hyperbolic GPU Marketplace.

## Features

- **Real-time GPU Availability**: Live access to Hyperbolic's GPU marketplace
- **Voice Interaction**: Natural conversation about GPU options and pricing
- **Smart Filtering**:
- Price ranges (budget to high-end)
- GPU quantities (1X to 8X+)
- Storage capacity
- Availability status
- **Dynamic Price Display**:
- Under $1: Shows in cents (e.g., "13¢/hr")
- $1 and above: Shows in dollars (e.g., "$1.50/hr")

## Available GPU Types

- Consumer GPUs (RTX 3070, 3080, 4090)
- Data Center GPUs (H100 SXM, NVIDIA H200)
- Various configurations (1X to 8X+)

## Requirements

- Python 3.12+
- Google API key (for Gemini)
- Daily.co API key
- Access to Hyperbolic Marketplace API

## Environment Setup

Create a `.env` file with:

```
GOOGLE_API_KEY=your_google_api_key
DAILY_API_KEY=your_daily_api_key
DAILY_SAMPLE_ROOM_URL=your_daily_room_url
```

## Installation

```bash
pip install -r requirements.txt
```

## Usage

Start the bot:

```bash
python main.py
```

Join the Daily.co room to interact with the bot. You can:

- Ask about available GPUs
- Filter by price range
- Sort by price (low to high or high to low)
- Filter by GPU quantity
- Check storage options
- Get real-time availability updates

## Example Queries

- "What GPUs are available?"
- "Show me budget options under 50 cents per hour"
- "What are your high-end GPUs?"
- "Do you have any 8X GPU configurations?"
- "Show me GPUs with over 500GB storage"
- "What's the price range for H100s?"

## Notes

- All GPU instances are located in US, North America
- Prices are always displayed per hour
- The bot automatically refreshes data for the most current availability
47 changes: 47 additions & 0 deletions multimodal-gpu_marketplace-bot/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
SYSTEM_INSTRUCTION = """
You are a helpful assistant for Hyperbolic Labs' GPU Marketplace. You can help users find and understand available GPU instances for rent.

You have access to the marketplace data through the get_available_gpus tool. When users ask about available GPUs, pricing, or specifications, use this tool to get the most current information.

Always be professional and helpful. When listing GPUs:
1. Mention the GPU model, memory, and hourly price
2. Indicate if the instance is currently available
3. Include the location/region

By default, only mention GPU model, memory, price, location, and availability. If a user wants to learn more about a specific instance, invite them to ask for details using the instance's GPU model or ID. When asked, provide all available technical details (CPU, storage, RAM, network, etc) for that instance in a clear, friendly, and expert manner.

Encourage users to ask about their use case (e.g., 'If you're doing XYZ, I recommend...') and offer expert advice as a pro GPU specialist. If a user describes their workload, suggest the best GPU for their needs and explain why.

If users ask about specific GPU models or price ranges, filter and highlight the relevant options from the data.

You can also see and analyze video feeds (screen share or camera) in real time. If a user shares their screen or camera, you can describe what is visible and help with on-screen tasks. Encourage users to share their screen for more detailed help.
"""

LLMCONTEXT_CONTENT = "Start by greeting me warmly and introducing me to GPU Rentals by Hyperbolic Labs and mention that you can do everything verbally. Encourage me to start by asking available GPU. Also mention that you can help me with my use case and suggest the best GPU for my needs. You can also see my screen or camera if I share it, and help with what you see!"

TOOLS = [
{
"function_declarations": [
{
"name": "get_available_gpus",
"description": "Get the list of available GPU instances in the marketplace",
"parameters": {
"type": "object",
"properties": {
"filter_type": {
"type": "string",
"enum": ["all", "available_only"],
"description": "Filter type for GPU instances",
}
},
"required": ["filter_type"],
},
}
]
}
]

REGION_MAP = {
"region-1": "US, North America",
# Add more mappings as needed
}
146 changes: 146 additions & 0 deletions multimodal-gpu_marketplace-bot/launcher.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import os
import subprocess
import sys
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
import uvicorn
from loguru import logger
import signal
import time

app = FastAPI()

# --- Global State Management ---
# This dictionary holds the state of our bot process.
# This simple in-memory state is perfect for a single Render instance.
bot_state = {
"process": None,
"status": "STOPPED", # Can be: STOPPED, STARTING, RUNNING, STOPPING
}

DAILY_ROOM_URL = os.getenv(
"DAILY_SAMPLE_ROOM_URL", "https://your-room.daily.co/default-room"
)


def get_page_html():
"""Generates the HTML for the control page based on the current bot state."""
status = bot_state["status"]

if status == "RUNNING":
return f"""
<h1>GPU Bot is <span style="color: green;">RUNNING</span></h1>
<p>The bot is active. You can now join the call.</p>
<a href="{DAILY_ROOM_URL}" target="_blank" class="button">Join Daily Room</a>
<form action="/stop-bot" method="post" style="margin-top: 20px;">
<button type="submit">Stop Bot Session</button>
</form>
"""
elif status == "STOPPED":
return """
<h1>GPU Bot is <span style="color: red;">STOPPED</span></h1>
<p>Click the button to start a new demo session.</p>
<form action="/start-bot" method="post">
<button type="submit">Start Bot Session</button>
</form>
"""
elif status == "STARTING":
return """
<h1>GPU Bot is <span style="color: orange;">STARTING...</span></h1>
<p>Please wait, this can take up to 30 seconds.</p>
<button disabled>Starting...</button>
"""
elif status == "STOPPING":
return """
<h1>GPU Bot is <span style="color: orange;">STOPPING...</span></h1>
<p>Please wait while the session is terminated.</p>
<button disabled>Stopping...</button>
"""


@app.get("/", response_class=HTMLResponse)
async def root():
"""Serves the main control page, which changes based on the bot's status."""
return f"""
<!DOCTYPE html><html><head><title>Bot Control</title>
<meta http-equiv="refresh" content="5"> <!-- Auto-refresh page every 5 seconds to update status -->
<style>
body {{ font-family: sans-serif; text-align: center; padding-top: 50px; background-color: #f0f0f0; }}
h1 {{ color: #333; }}
p {{ color: #666; }}
button, .button {{ display: inline-block; font-size: 1.2em; padding: 12px 24px; cursor: pointer; border: none; border-radius: 5px; text-decoration: none; color: white; }}
button {{ background-color: #28a745; }}
form button {{ background-color: #dc3545; }}
.button {{ background-color: #007bff; }}
button:disabled {{ background-color: #cccccc; cursor: not-allowed; }}
</style>
</head><body>
<div id="control-panel">
{get_page_html()}
</div>
</body></html>
"""


@app.post("/start-bot")
async def start_bot_endpoint():
"""Endpoint to launch the bot. Prevents starting if not STOPPED."""
if bot_state["status"] != "STOPPED":
logger.warning(f"Attempted to start bot while in state: {bot_state['status']}")
return RedirectResponse(url="/", status_code=303)

bot_state["status"] = "STARTING"
logger.info("Bot state changed to STARTING.")

try:
script_path = os.path.join(os.path.dirname(__file__), "main.py")
python_executable = sys.executable
process = subprocess.Popen([python_executable, script_path])

bot_state["process"] = process
# Give it a moment to stabilize before changing state to RUNNING
time.sleep(5) # A small delay to let the process actually start
bot_state["status"] = "RUNNING"
logger.info(
f"Bot process started with PID {process.pid}. State is now RUNNING."
)

except Exception as e:
logger.error(f"Failed to launch bot process: {e}")
bot_state["status"] = "STOPPED"

return RedirectResponse(url="/", status_code=303)


@app.post("/stop-bot")
async def stop_bot_endpoint():
"""Endpoint to stop the bot. Prevents stopping if not RUNNING."""
if bot_state["status"] != "RUNNING":
logger.warning(f"Attempted to stop bot while in state: {bot_state['status']}")
return RedirectResponse(url="/", status_code=303)

bot_state["status"] = "STOPPING"
logger.info("Bot state changed to STOPPING.")

process = bot_state["process"]
if process and process.poll() is None:
logger.info(f"Sending SIGTERM to bot process with PID: {process.pid}")
process.send_signal(signal.SIGTERM)
try:
process.wait(timeout=15)
logger.info("Bot process terminated gracefully.")
except subprocess.TimeoutExpired:
logger.warning("Bot did not terminate in time, sending SIGKILL.")
process.kill()

bot_state["process"] = None
bot_state["status"] = "STOPPED"
logger.info("Bot state changed to STOPPED.")

return RedirectResponse(url="/", status_code=303)


if __name__ == "__main__":
port = int(os.environ.get("PORT", 8080))
logger.info(f"Starting bot launcher on http://0.0.0.0:{port}")
uvicorn.run(app, host="0.0.0.0", port=port)
Loading