diff --git a/.gitignore b/.gitignore
index 1aad045e..4320e139 100644
--- a/.gitignore
+++ b/.gitignore
@@ -184,3 +184,6 @@ wandb
.gradio/
example_trainer/trained_model_checkpoints/
+
+# uv
+uv.lock
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0957bb8e..e35e264c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,7 +13,7 @@ We welcome any type of contribution, not just code. You can help with:
We use GitHub to host the code, track issues and feature requests, and accept pull requests.
## We Use GitHub Flow
-We follow the [GitHub Flow](https://guides.github.com/introduction/flow/index.html) development workflow. All code changes happen through Pull Requests.
+We follow the [GitHub Flow](https://docs.github.com) development workflow. All code changes happen through Pull Requests.
## Getting Started
@@ -126,6 +126,12 @@ Any contributions you make will be under the Apache License 2.0. In short, when
Since Atropos is focused on reinforcement learning environments, we encourage contributions of new training environments. However, please adhere to the following guidelines:
+* **Directory Structure**: Please create your new environment within the `environments/community/` subdirectory. This helps us organize incoming contributions and allows for a streamlined initial review process before full testing and integration.
+* **Import Style**: We prefer that you treat your environment's directory as the package root for imports. For example, if your environment resides in `environments/community/my_new_env/` and you need to import `SomeClass` (assuming it's in a `some_file_in_my_env.py` file at the root of your `my_new_env` directory or accessible via your Python path setup), you should be able to use a direct import like:
+ ```python
+ from some_file_in_my_env import SomeClass
+ ```
+ This convention applies to imports within your environment's own files and any helper modules you create alongside it.
* **Legal compliance**: Do not submit environments that involve illegal activities or content.
* **GitHub compliance**: All contributions must comply with [GitHub's Terms of Service and Community Guidelines](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service).
diff --git a/README.md b/README.md
index 31c55bb2..9374e2fa 100644
--- a/README.md
+++ b/README.md
@@ -133,12 +133,12 @@ Environment Used: [https://github.com/NousResearch/atropos/blob/main/environment
| Category | Description |
|----------|------------|
| 📁 [`atroposlib/`](atroposlib/) | Core library containing base classes and utilities |
-| 🎮 [`environments/`](environments/) | Collection of ready-to-use RL environments |
+| 🎮 [`environments/`](environments/) | Collection of ready-to-use RL environments. Community contributions are typically placed in the [`environments/community/`](environments/community/) subdirectory. |
| 📚 [`example_trainer/`](example_trainer/) | Example training scripts and configurations |
Key Documents:
- [Base Environment Class](atroposlib/envs/README.md) - Documentation for creating custom environments
-- [Environments Overview](environments/README.md) - Documentation for existing environments
+- [Environments Overview and Contribution Guide](environments/community/README.md) - Documentation for existing environments and how to contribute new ones.
- [Full Environment Config Options](CONFIG.md) - Documentation for creating custom environments
- [Example Trainer](example_trainer/README.md) - Getting started with training
- [Slurm Guide](SLURM.md) - Guide for using Atropos with Slurm for distributed inference
diff --git a/atroposlib/envs/server_handling/openai_server.py b/atroposlib/envs/server_handling/openai_server.py
index cc541d7c..33322262 100644
--- a/atroposlib/envs/server_handling/openai_server.py
+++ b/atroposlib/envs/server_handling/openai_server.py
@@ -164,9 +164,10 @@ def resolve_openai_configs(
if (is_multi_server_yaml or is_multi_server_default) and openai_cli_config:
raise FailedExecutionException(
- f"CLI overrides for OpenAI settings (--{openai_full_prefix}*) are not supported "
+ message=f"CLI overrides for OpenAI settings (--{openai_full_prefix}*) are not supported "
f"when multiple servers are defined (either via YAML list under '{OPENAI_NAMESPACE}' "
- "or a default list with length >= 2)."
+ "or a default list with length >= 2).",
+ exit_code=2,
)
if is_multi_server_yaml:
diff --git a/environments/community/README.md b/environments/community/README.md
new file mode 100644
index 00000000..b09e963d
--- /dev/null
+++ b/environments/community/README.md
@@ -0,0 +1,148 @@
+# Community Environments
+
+This directory is home to community-contributed training environments for Atropos. Environments submitted by the community will be placed here after an initial code review.
+
+**Note:** Environments in this directory are pending full testing and integration. While they have passed a basic code check, they may not yet have been rigorously validated on our compute cluster.
+
+## Contributing Your Environment
+
+We encourage you to contribute your own RL environments! When developing a new environment, please follow these guidelines:
+
+1. **Create your environment in this `environments/community/` subdirectory.** This helps us keep new submissions organized.
+2. **Preferred Import Style:** We prefer that you treat your environment's directory as the package root for imports within your environment code. For example, if you need to import `SomeClass`, you can do so directly:
+ ```python
+ from some_file_in_my_env import SomeClass
+ ```
+ This helps maintain consistency and makes it easier to integrate your environment.
+
+### Environment Standards
+
+Community environments should:
+- Include clear documentation and setup instructions
+- Specify all dependencies in requirements files
+- Provide example configurations and usage
+- Follow the AtroposBaseEnv pattern for consistency
+- Include appropriate error handling and validation
+
+### Submission Process
+
+To contribute a new environment to the community collection:
+
+1. **Fork the repository** and create a new branch
+2. **Add your environment** to this `community/` directory
+3. **Include comprehensive documentation**:
+ - README with setup instructions
+ - Requirements file for dependencies
+ - Example usage and configuration
+4. **Follow naming conventions**:
+ - Use descriptive directory names for complex environments
+ - Single file environments should have descriptive names
+5. **Test thoroughly** before submitting
+6. **Submit a pull request** with a clear description
+
+Once your environment is ready, please follow the guidelines in our main [CONTRIBUTING.md](../../../CONTRIBUTING.md) to submit your contribution.
+
+---
+
+## Available Environments
+
+### 1. Lean Proof Environment (`lean_proof_env/`)
+**Author**: [GabinFay](https://github.com/GabinFay)
+**Purpose**: Testing Language Learning Models (LLMs) on Lean theorem proving tasks
+
+A comprehensive environment for evaluating LLMs on formal mathematical reasoning using the Lean theorem prover. Features include:
+- Support for custom problem datasets or MiniF2F benchmark
+- Integration with Lean 4 theorem prover
+- Configurable difficulty levels and problem sets
+- Automated proof validation
+
+**Requirements**: Lean 4 installation, OpenAI API key
+
+### 2. Router Environment (`router_env/`)
+**Author**: [GabinFay](https://github.com/GabinFay)
+**Purpose**: Multi-agent routing and coordination system
+
+A sophisticated environment for testing agent routing and coordination capabilities. Includes:
+- Multiple specialized agents (calendar, contact, Gmail, telephony, etc.)
+- Model Contextualized Protocol (MCP) tools integration
+- Spotify, Google Maps, and Perplexity integrations
+- Complex multi-turn conversation handling
+
+**Features**:
+- Telephony agent with inbound/outbound call handling
+- Calendar and contact management
+- Memory and calculation agents
+- Router agent for intelligent task delegation
+
+### 3. Philosophical RLAIF Environment (`philosophical_rlaif_env.py`)
+**Author**: [GabinFay](https://github.com/GabinFay)
+**Purpose**: Reinforcement Learning from AI Feedback (RLAIF) for philosophical reasoning
+
+An environment focused on training models for deep philosophical inquiry and reasoning. Features:
+- Deep thinking prompts with systematic reasoning processes
+- Preference learning for philosophical depth and nuance
+- Multi-perspective analysis and assumption questioning
+- Evaluation of response quality for philosophical discussions
+
+**Capabilities**:
+- Generates paired responses for preference comparison
+- Uses judge models to evaluate philosophical depth
+- Tracks preference consistency and reasoning quality
+- Supports WandB logging for training insights
+
+### 4. Playwright Agent Environment (`playwright_agent_env.py`)
+**Author**: [erikqu](https://github.com/erikqu)
+**Purpose**: Web automation and browser interaction for LLM agents
+
+A comprehensive environment for training LLMs to interact with web pages through browser automation. Features:
+- Playwright-based browser control with headless operation
+- Screenshot-based visual input for LLM decision making
+- JSON-based action commands (navigate, click, type, finish)
+- Video recording of browser sessions for evaluation
+- Google Gemini integration for success evaluation
+
+**Capabilities**:
+- Loads tasks from WebVoyager dataset or custom task definitions
+- Supports development mode for testing without LLM calls
+- Automatic reward computation based on success and efficiency
+- Comprehensive error handling and fallback mechanisms
+- Integration with Atropos training pipeline
+
+**Requirements**: Playwright, optional Google Gemini API for evaluation
+
+### 5. Metric Card Generator Environment (`metric_card_generator/`)
+**Author**: [vivek100](https://github.com/vivek100)
+**Purpose**: Structured JSON generation for AI model evaluation dashboards
+
+A comprehensive environment for training LLMs to generate well-structured JSON configurations for Metric Card UI components. Features:
+- Closed-loop generation, evaluation, and visualization pipeline
+- Schema validation for JSON metric card configurations
+- Multi-dimensional evaluation (validity, compliance, semantic quality)
+- Support for various business domains and metric types
+- WandB integration for performance tracking
+
+**Capabilities**:
+- Generates metric cards for diverse business contexts (e-commerce, finance, healthcare, etc.)
+- Validates JSON structure against predefined schemas
+- Evaluates semantic quality and formatting consistency
+- Provides training data extraction and filtering utilities
+- Includes visualization tools for score distribution analysis
+
+**Components**:
+- `metric_card_generator.py`: Main environment implementation
+- `extract_metric_training.py`: Training data extraction utility
+- `trainingDataScript.py`: Dataset creation from collected examples
+- `show_score_distribution.py`: Performance analysis visualization
+
+**Requirements**: Pydantic, tqdm
+
+---
+
+## Support
+
+For questions or issues with community environments:
+- Check the individual environment's README first
+- Open an issue in the main repository
+- Tag the environment author if possible
+
+*These environments are community contributions and may have different maintenance levels and support compared to core Atropos environments.*
diff --git a/environments/community/lean_proof_env/README.md b/environments/community/lean_proof_env/README.md
new file mode 100644
index 00000000..16ff1e30
--- /dev/null
+++ b/environments/community/lean_proof_env/README.md
@@ -0,0 +1,64 @@
+# Lean Theorem Proving Environment for Atropos
+
+This environment allows testing Language Learning Models (LLMs) on Lean theorem proving tasks using `atroposlib`.
+
+## Setup
+
+1. **Atroposlib**: Ensure `atroposlib` is installed. If you are working within the main `atropos` repository, this is typically done by navigating to the `atropos` root directory (i.e., `cd LeanCopilot/atropos`) and running:
+ ```bash
+ pip install -e .
+ ```
+
+2. **Python Dependencies**: Navigate to this environment's directory (`LeanCopilot/atropos/environments/lean_proof_env/`) and install the required Python packages using the provided `requirements.txt`:
+ ```bash
+ pip install -r requirements.txt
+ ```
+ This will install `datasets`, `wandb`, `tqdm`, and `python-dotenv`.
+
+3. **OpenAI API Key**: This environment requires an OpenAI API key. You can set it in one of two ways:
+ * As an environment variable:
+ ```bash
+ export OPENAI_API_KEY="your_actual_openai_api_key"
+ ```
+ * Create a `.env` file within this directory (`LeanCopilot/atropos/environments/lean_proof_env/.env`) with the following content:
+ ```
+ OPENAI_API_KEY=your_actual_openai_api_key
+ ```
+ The script will automatically load this if the environment variable is not set.
+
+4. **Lean Installation**: Ensure Lean 4 is installed and the `lean` executable is in your system's PATH. You can find installation instructions on the [Lean official website](https://leanprover.github.io/lean4/doc/setup.html).
+
+## Running the Environment
+
+Make sure your current working directory is this environment's directory (`LeanCopilot/atropos/environments/lean_proof_env/`).
+
+Run the environment script using the `process` command provided by `AtroposBaseEnv`:
+
+```bash
+python lean_proof_env.py process \
+ --env.lean_problem_dataset_name="internal_simple_test" \
+ --env.total_steps=5 \
+ --env.group_size=1 \
+ --env.wandb_name="lean_simple_test_run" \
+ --openai.model_name="gpt-4o"
+```
+
+### Command-Line Arguments:
+
+* `--env.lean_problem_dataset_name`:
+ * `"internal_simple_test"`: Uses a small set of hardcoded simple theorems (defined within the script).
+ * `"Tonic/MiniF2F"`: Attempts to load the MiniF2F benchmark from Hugging Face datasets. You might need to specify `--env.lean_problem_dataset_split` (e.g., "test" or "train").
+* `--env.total_steps`: Number of problems (or items) to process. The `internal_simple_test` set has 8 problems; if `total_steps` is less, a random subset will be chosen for that many steps.
+* `--env.group_size`: Number of LLM attempts per problem.
+* `--env.wandb_name`: Name for the WandB run if `use_wandb` is enabled (e.g., by passing `--env.use_wandb=True`).
+* `--openai.model_name`: The OpenAI model to use (e.g., "gpt-4o", "gpt-4-turbo", "gpt-3.5-turbo"). This overrides the default in `config_init`.
+* `--openai.api_key`: Can be passed explicitly, but using the environment variable or `.env` file is recommended and more secure.
+* `--openai.base_url`: If using a custom OpenAI-compatible API endpoint.
+
+The script will generate a `.jsonl` file with the trajectories and an HTML visualization of the rollouts. By default, these are saved in a `data/` subdirectory created within the current working directory (e.g., `LeanCopilot/atropos/environments/lean_proof_env/data/`).
+
+## Customization
+
+* **Problem Sets**: Modify the `setup()` method in `lean_proof_env.py` to load different Lean problems or datasets.
+* **LLM Prompts**: Adjust the `get_next_item()` method in `lean_proof_env.py` to change the prompts sent to the LLM.
+* **Configuration**: Default configurations are defined in the `LeanProofEnvConfig` class within `lean_proof_env.py`. Most of these can be overridden by command-line arguments (e.g., `--env.max_proof_generation_tokens=256`).
diff --git a/environments/community/lean_proof_env/lean_proof_env.py b/environments/community/lean_proof_env/lean_proof_env.py
new file mode 100644
index 00000000..056dccea
--- /dev/null
+++ b/environments/community/lean_proof_env/lean_proof_env.py
@@ -0,0 +1,644 @@
+import asyncio
+import os
+import random
+import re
+import tempfile
+import uuid
+from concurrent.futures import ProcessPoolExecutor
+from typing import Any, Dict, List, Optional, Tuple
+
+# import wandb # Conditionally import later
+# from datasets import load_dataset # Conditionally import later
+from pydantic import Field
+
+from atroposlib.envs.base import (
+ APIServerConfig as AtroposAPIServerConfig, # Renamed to avoid conflict if needed later
+)
+from atroposlib.envs.base import BaseEnv as AtroposBaseEnv
+from atroposlib.envs.base import BaseEnvConfig as AtroposBaseEnvConfig
+from atroposlib.envs.base import EvalHandlingEnum, ScoredDataGroup
+
+# Global variable to hold wandb if imported
+wandb = None
+load_dataset = None # Placeholder for conditional import
+python_dotenv_available = False
+try:
+ from dotenv import load_dotenv
+
+ python_dotenv_available = True
+except ImportError:
+ pass
+
+
+class LeanProofEnvConfig(AtroposBaseEnvConfig): # Inherit from actual Atropos config
+ tokenizer_name: str = Field("Salesforce/codegen-350M-mono")
+ group_size: int = Field(8)
+ use_wandb: bool = Field(False)
+ total_steps: int = Field(
+ 10
+ ) # For process mode, this might be interpreted as number of items to process
+ batch_size: int = Field(2)
+ steps_per_eval: int = Field(1)
+ max_token_length: int = Field(
+ 1536
+ ) # Max length for tokenizer input, not necessarily LLM generation
+ wandb_name: str = Field("lean_proof_rl_minif2f")
+ eval_handling: EvalHandlingEnum = Field(EvalHandlingEnum.LIMIT_TRAIN)
+ eval_limit_ratio: float = Field(0.1)
+ lean_executable_path: str = Field("lean")
+ lean_problem_dataset_name: Optional[str] = Field("internal_simple_test")
+ lean_problem_dataset_split: str = Field("train")
+ num_rollouts_to_keep: int = Field(5) # For WandB table logging
+ num_cpus_maxtasksperchild: int = Field(1)
+ max_proof_generation_tokens: int = Field(
+ 512, description="Maximum tokens for the LLM to generate for a proof attempt."
+ )
+ proof_verification_timeout_seconds: int = Field(
+ 60, description="Timeout for Lean proof verification."
+ )
+ # Add any other config fields specific to LeanProofEnv or expected by AtroposBaseEnvConfig
+
+
+async def verify_lean_proof(
+ lean_executable_path: str, proof_content: str, timeout_seconds: int = 60
+) -> Tuple[bool, str]:
+ """
+ Verifies a Lean proof by writing it to a temporary file and running Lean.
+ Returns (True, "success") if proof is valid, (False, error_message) otherwise.
+ """
+ common_imports = "" # Empty for basic examples
+ full_content_for_lean = common_imports + proof_content
+
+ with tempfile.NamedTemporaryFile(
+ mode="w", suffix=".lean", delete=False, encoding="utf-8"
+ ) as tmp_file:
+ tmp_file.write(full_content_for_lean)
+ tmp_file_name = tmp_file.name
+
+ process = None # Ensure process is defined for finally block
+ try:
+ process = await asyncio.create_subprocess_exec(
+ lean_executable_path,
+ tmp_file_name,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE,
+ )
+ # Wait for communicate with a timeout
+ stdout, stderr = await asyncio.wait_for(
+ process.communicate(), timeout=timeout_seconds
+ )
+
+ if process.returncode == 0:
+ return True, "success"
+ else:
+ error_output = (
+ stderr.decode("utf-8", errors="ignore").strip()
+ if stderr
+ else stdout.decode("utf-8", errors="ignore").strip()
+ )
+ error_output = error_output.replace(
+ tmp_file_name + ":", ""
+ ) # Remove file path from error
+ return False, (
+ error_output
+ if error_output
+ else "Lean verification failed with non-zero exit code and no error message."
+ )
+ except FileNotFoundError:
+ error_msg = (
+ f"Lean executable not found at {lean_executable_path}. "
+ "Please ensure Lean is installed and in PATH, or configure lean_executable_path."
+ )
+ return False, error_msg
+ except asyncio.TimeoutError:
+ if process and process.returncode is None: # Check if process is still running
+ try:
+ process.kill()
+ await process.wait() # Ensure process is cleaned up
+ except ProcessLookupError:
+ pass # Process already terminated
+ except Exception as e_kill:
+ print(f"Error killing timed-out Lean process: {e_kill}")
+ return False, f"Lean verification timed out after {timeout_seconds} seconds."
+ except Exception as e:
+ return False, f"Error during Lean verification: {str(e)}"
+ finally:
+ if (
+ process and process.returncode is None
+ ): # Ensure process is terminated if loop exited early
+ try:
+ process.kill()
+ await process.wait()
+ except ProcessLookupError:
+ pass # Process already terminated
+ except Exception as e_kill_finally:
+ print(f"Error killing Lean process in finally: {e_kill_finally}")
+ if os.path.exists(tmp_file_name):
+ try:
+ os.remove(tmp_file_name)
+ except Exception as e_remove:
+ print(
+ f"Warning: Could not remove temporary file {tmp_file_name}: {e_remove}"
+ )
+
+
+class LeanProofEnv(AtroposBaseEnv): # Inherit from actual Atropos BaseEnv
+ name = "lean_proof"
+ env_config_cls = LeanProofEnvConfig
+
+ def __init__(
+ self,
+ config: LeanProofEnvConfig,
+ server_configs: List[
+ AtroposAPIServerConfig
+ ], # Use renamed AtroposAPIServerConfig
+ slurm=True, # Default slurm to True as in original BaseEnv, can be overridden
+ testing=False, # Default testing to False
+ ):
+ global wandb
+ self.wandb_available = False
+ if hasattr(config, "use_wandb") and config.use_wandb:
+ try:
+ import wandb as wb
+
+ wandb = wb
+ self.wandb_available = True
+ except ImportError:
+ print(
+ "Warning: wandb could not be imported. wandb logging will be disabled."
+ )
+ config.use_wandb = False
+
+ print("Initializing LeanProofEnv with Atropos...")
+ super().__init__(config, server_configs, slurm=slurm, testing=testing)
+
+ try:
+ self.mp_executor = ProcessPoolExecutor(config.num_cpus_maxtasksperchild)
+ except (AttributeError, TypeError):
+ print(
+ "Warning: could not create ProcessPoolExecutor with "
+ "config.num_cpus_maxtasksperchild. Using default."
+ )
+ self.mp_executor = ProcessPoolExecutor(
+ max_workers=(
+ config.num_cpus_maxtasksperchild
+ if hasattr(config, "num_cpus_maxtasksperchild")
+ else 1
+ )
+ )
+
+ self.eval_metrics = list()
+ self.pass_at_groupsize = list()
+ self.successful_proofs_rollouts = list()
+ self.failed_proofs_rollouts = list()
+ self.iter = 0
+ self.problems = []
+ self.rollouts_table = None
+
+ async def chat_completion(self, *args, **kwargs):
+ if not self.server:
+ raise RuntimeError(
+ "Server not initialized. Ensure AtroposBaseEnv sets up self.server."
+ )
+ return await self.server.chat_completion(*args, **kwargs)
+
+ @classmethod
+ def config_init(cls) -> Tuple[LeanProofEnvConfig, List[AtroposAPIServerConfig]]:
+ env_config = LeanProofEnvConfig(
+ tokenizer_name="Salesforce/codegen-350M-mono",
+ group_size=8,
+ use_wandb=False,
+ total_steps=10,
+ batch_size=2,
+ steps_per_eval=1,
+ max_token_length=1536,
+ wandb_name="lean_proof_rl_new_env", # Changed wandb_name for clarity
+ eval_handling=EvalHandlingEnum.LIMIT_TRAIN,
+ eval_limit_ratio=0.1,
+ lean_executable_path="lean",
+ lean_problem_dataset_name="internal_simple_test",
+ lean_problem_dataset_split="train",
+ num_rollouts_to_keep=5,
+ num_cpus_maxtasksperchild=1,
+ max_proof_generation_tokens=512,
+ )
+
+ openai_api_key = os.getenv("OPENAI_API_KEY")
+ if not openai_api_key and python_dotenv_available:
+ print(
+ "OPENAI_API_KEY not found in environment, attempting to load from .env file..."
+ )
+ load_dotenv() # Load .env file from current directory
+ openai_api_key = os.getenv("OPENAI_API_KEY")
+
+ if not openai_api_key:
+ raise ValueError(
+ "OPENAI_API_KEY not found in environment variables or .env file. "
+ "Please set it to run this environment. You can create a .env file "
+ "in the execution directory with OPENAI_API_KEY='your_key'."
+ )
+
+ server_configs = [
+ AtroposAPIServerConfig(
+ model_name="gpt-4o", # Default model, can be overridden by CLI
+ base_url="https://api.openai.com/v1",
+ api_key=openai_api_key,
+ ),
+ ]
+ print(
+ "INFO: LeanProofEnv will use OpenAI model (default/from CLI) with API key from env/dotenv."
+ )
+ return env_config, server_configs
+
+ async def wandb_log(self, wandb_metrics: Optional[Dict] = None):
+ if self.wandb_available and wandb and wandb_metrics:
+ print(f"[WandB] Metrics: {wandb_metrics}")
+ elif wandb_metrics:
+ print(f"[Metrics (wandb disabled)] {wandb_metrics}")
+
+ async def setup(self):
+ global load_dataset
+ if self.config.lean_problem_dataset_name == "internal_simple_test":
+ print("Using internal_simple_test: Loading hardcoded simple Lean problems.")
+ self.problems = [
+ {
+ "name": "trivial_true",
+ "formal_statement": "theorem trivial_true : True :=\n trivial",
+ "theorem_header": "theorem trivial_true : True :=",
+ "proof_prefix": "theorem trivial_true : True :=\n",
+ "statement_to_prove": "True",
+ },
+ {
+ "name": "rfl_nat_add_zero",
+ "formal_statement": "theorem rfl_nat_add_zero (n : Nat) : n + 0 = n :=\n rfl",
+ "theorem_header": "theorem rfl_nat_add_zero (n : Nat) : n + 0 = n :=",
+ "proof_prefix": "theorem rfl_nat_add_zero (n : Nat) : n + 0 = n :=\n",
+ "statement_to_prove": "(n : Nat) : n + 0 = n",
+ },
+ {
+ "name": "exact_refl",
+ "formal_statement": "theorem exact_refl (P : Prop) (h : P) : P :=\n exact h",
+ "theorem_header": "theorem exact_refl (P : Prop) (h : P) : P :=",
+ "proof_prefix": "theorem exact_refl (P : Prop) (h : P) : P :=\n",
+ "statement_to_prove": "(P : Prop) (h : P) : P",
+ },
+ {
+ "name": "id_apply",
+ "formal_statement": "theorem id_apply (P : Prop) (h : P) : P :=\n h",
+ "theorem_header": "theorem id_apply (P : Prop) (h : P) : P :=",
+ "proof_prefix": "theorem id_apply (P : Prop) (h : P) : P :=\n",
+ "statement_to_prove": "(P : Prop) (h : P) : P",
+ },
+ {
+ "name": "nat_add_comm",
+ "formal_statement": "theorem nat_add_comm (n m : Nat) : n + m = m + n :=\n Nat.add_comm n m",
+ "theorem_header": "theorem nat_add_comm (n m : Nat) : n + m = m + n :=",
+ "proof_prefix": "theorem nat_add_comm (n m : Nat) : n + m = m + n :=\n",
+ "statement_to_prove": "(n m : Nat) : n + m = m + n",
+ },
+ {
+ "name": "true_intro_example",
+ "formal_statement": "theorem true_intro_example : True :=\n True.intro",
+ "theorem_header": "theorem true_intro_example : True :=",
+ "proof_prefix": "theorem true_intro_example : True :=\n",
+ "statement_to_prove": "True",
+ },
+ {
+ "name": "and_intro_example",
+ "formal_statement": (
+ "theorem and_intro_example (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q :=\n And.intro hp hq"
+ ),
+ "theorem_header": "theorem and_intro_example (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q :=",
+ "proof_prefix": "theorem and_intro_example (P Q : Prop) (hp : P) (hq : Q) : P ∧ Q :=\n",
+ "statement_to_prove": "(P Q : Prop) (hp : P) (hq : Q) : P ∧ Q",
+ },
+ {
+ "name": "list_nil_is_empty_example",
+ "formal_statement": (
+ "theorem list_nil_is_empty_example {α : Type} : "
+ "List.isEmpty ([] : List α) :=\n rfl"
+ ),
+ "theorem_header": (
+ "theorem list_nil_is_empty_example {α : Type} : "
+ "List.isEmpty ([] : List α) :="
+ ),
+ "proof_prefix": (
+ "theorem list_nil_is_empty_example {α : Type} : "
+ "List.isEmpty ([] : List α) :=\n"
+ ),
+ "statement_to_prove": "{α : Type} : List.isEmpty ([] : List α)",
+ },
+ ]
+ print(f"Loaded {len(self.problems)} simple hardcoded problems.")
+ return
+ if load_dataset is None:
+ try:
+ from datasets import load_dataset as ld
+
+ load_dataset = ld
+ print("Successfully imported load_dataset from datasets library.")
+ except ImportError:
+ print(
+ "Error: The 'datasets' library is not installed. "
+ "Please install it with 'pip install datasets' to use the MiniF2F benchmark."
+ )
+ self.problems = [
+ {
+ "name": "dummy_add_zero_no_dataset_lib",
+ "formal_statement": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=\n sorry",
+ "theorem_header": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=",
+ "proof_prefix": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=\n",
+ "statement_to_prove": "(n : Nat) : n + 0 = n",
+ }
+ ]
+ print(
+ f"Using {len(self.problems)} hardcoded problem due to missing 'datasets' library."
+ )
+ return
+ if self.config.lean_problem_dataset_name:
+ print(
+ f"Attempting to load dataset: {self.config.lean_problem_dataset_name} "
+ f"split: {self.config.lean_problem_dataset_split}"
+ )
+ try:
+ dataset = load_dataset(
+ self.config.lean_problem_dataset_name,
+ split=self.config.lean_problem_dataset_split,
+ trust_remote_code=True,
+ )
+ processed_problems = []
+ for i, item in enumerate(dataset):
+ formal_statement = item.get("formal_statement")
+ if not formal_statement or not isinstance(formal_statement, str):
+ print(
+ f"Skipping item {i} due to missing or invalid formal_statement: {item}"
+ )
+ continue
+ name_match = re.search(r"theorem\s+([\w_]+)", formal_statement)
+ problem_name = (
+ name_match.group(1) if name_match else f"minif2f_problem_{i}"
+ )
+ proof_start_marker = ":="
+ if proof_start_marker in formal_statement:
+ header_part, _ = formal_statement.split(proof_start_marker, 1)
+ theorem_header = header_part.strip() + f" {proof_start_marker}"
+ proof_prefix = theorem_header + "\n"
+ statement_to_prove_match = re.search(
+ r"theorem\s+[\w_]+\s*(.*)\s*:=", theorem_header
+ )
+ statement_to_prove = (
+ statement_to_prove_match.group(1).strip()
+ if statement_to_prove_match
+ else ""
+ )
+ else:
+ print(
+ f"Warning: Could not find ':=' in formal_statement for {problem_name}. "
+ "Using full statement as header."
+ )
+ theorem_header = formal_statement.strip()
+ proof_prefix = theorem_header + "\n"
+ statement_to_prove = formal_statement
+ processed_problems.append(
+ {
+ "name": problem_name,
+ "formal_statement": formal_statement,
+ "theorem_header": theorem_header,
+ "proof_prefix": proof_prefix,
+ "statement_to_prove": statement_to_prove,
+ }
+ )
+ self.problems = processed_problems
+ print(
+ f"Loaded and processed {len(self.problems)} problems from "
+ f"{self.config.lean_problem_dataset_name}."
+ )
+ except Exception as e:
+ print(
+ f"Failed to load or process dataset {self.config.lean_problem_dataset_name}: {e}. "
+ "Using hardcoded examples."
+ )
+ self.problems = []
+ if not self.problems:
+ self.problems = [
+ {
+ "name": "dummy_add_zero",
+ "formal_statement": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=\n sorry",
+ "theorem_header": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=",
+ "proof_prefix": "theorem dummy_add_zero (n : Nat) : n + 0 = n :=\n",
+ "statement_to_prove": "(n : Nat) : n + 0 = n",
+ },
+ {
+ "name": "dummy_true",
+ "formal_statement": "theorem dummy_true : True :=\n trivial",
+ "theorem_header": "theorem dummy_true : True :=",
+ "proof_prefix": "theorem dummy_true : True :=\n",
+ "statement_to_prove": "True",
+ },
+ ]
+ print(
+ f"Using {len(self.problems)} hardcoded problems due to failure in "
+ "dataset loading or processing."
+ )
+
+ async def get_next_item(self) -> Dict[str, Any]:
+ if not self.problems:
+ print(
+ "Error: No problems loaded. Cannot get next item. "
+ "Ensure dataset is configured and loaded correctly."
+ )
+ return {
+ "history": [
+ {
+ "role": "system",
+ "content": "You are a Lean theorem prover. Error: No problems available.",
+ }
+ ],
+ "model_name": (
+ self.server.servers[0].config.model_name
+ if self.server.servers and hasattr(self.server.servers[0], "config")
+ else "error_model"
+ ),
+ "item_uuid": str(uuid.uuid4()),
+ "env_specific_info": {"problem_name": "dummy_no_problems_loaded"},
+ "metadata": {},
+ "max_tokens": self.config.max_proof_generation_tokens,
+ }
+ problem = random.choice(self.problems)
+ history = [
+ {
+ "role": "system",
+ "content": (
+ "You are an expert Lean theorem prover. Complete the given Lean proof. "
+ "Only output the proof steps after the `:=` a single newline. "
+ "Do not repeat the theorem statement."
+ ),
+ },
+ {"role": "user", "content": problem["proof_prefix"]},
+ ]
+ return {
+ "history": history,
+ "model_name": (
+ self.server.servers[0].config.model_name
+ if self.server.servers and hasattr(self.server.servers[0], "config")
+ else "default_model"
+ ),
+ "item_uuid": str(uuid.uuid4()),
+ "env_specific_info": problem,
+ "metadata": {"problem_name": problem["name"]},
+ "max_tokens": self.config.max_proof_generation_tokens,
+ }
+
+ async def evaluate(self, *args, **kwargs):
+ print(f"Evaluate called with args: {args}, kwargs: {kwargs}")
+ eval_metrics = {"placeholder_eval_metric": random.random()}
+ if self.wandb_available and wandb:
+ await self.wandb_log({"eval": eval_metrics})
+ else:
+ print(f"[Metrics (wandb disabled) - Eval] {eval_metrics}")
+ self.eval_metrics.append(eval_metrics)
+
+ async def verify_lean_proof(
+ self, theorem_header: str, proof_completion: str, timeout_seconds: int = 60
+ ) -> Tuple[bool, str]:
+ full_proof = theorem_header + "\n" + proof_completion
+ print(
+ f"LeanProofEnv.verify_lean_proof attempting to verify: {repr(full_proof)}"
+ )
+ return await verify_lean_proof(
+ lean_executable_path=self.config.lean_executable_path,
+ proof_content=full_proof,
+ timeout_seconds=timeout_seconds,
+ )
+
+ async def collect_trajectories(
+ self, item: Dict[str, Any]
+ ) -> Tuple[Optional[ScoredDataGroup], List[Dict[str, Any]]]:
+ problem_data = item["env_specific_info"]
+ prompt_messages = item["history"]
+ prompt_text_for_tokenizer = "\n".join(
+ [msg["content"] for msg in prompt_messages if "content" in msg]
+ )
+ llm_raw_outputs = []
+ try:
+ llm_output_response = await self.chat_completion(
+ messages=prompt_messages,
+ model=item["model_name"],
+ n=self.config.group_size,
+ max_tokens=item.get(
+ "max_tokens", self.config.max_proof_generation_tokens
+ ),
+ temperature=0.7,
+ )
+ if llm_output_response.choices:
+ llm_raw_outputs = [
+ choice.message.content
+ for choice in llm_output_response.choices
+ if choice.message.content is not None
+ ]
+ else:
+ print(
+ f"Warning: LLM output for item {item.get('item_uuid')} has no choices."
+ )
+ llm_raw_outputs = [""] * self.config.group_size
+ except Exception as e:
+ print(f"Error during LLM call for item {item.get('item_uuid')}: {e}")
+ llm_raw_outputs = [f"LLM_ERROR: {e}"] * self.config.group_size
+ scores = []
+ completions_data = []
+ processed_messages_for_html = []
+ for i, raw_llm_output in enumerate(llm_raw_outputs):
+ cleaned_proof_steps = raw_llm_output.strip()
+ if cleaned_proof_steps.startswith("```lean"):
+ cleaned_proof_steps = cleaned_proof_steps[len("```lean") :].strip()
+ elif cleaned_proof_steps.startswith("```"):
+ cleaned_proof_steps = cleaned_proof_steps[len("```") :].strip()
+ if cleaned_proof_steps.endswith("```"):
+ cleaned_proof_steps = cleaned_proof_steps[: -len("```")].strip()
+ theorem_header = problem_data["theorem_header"]
+ is_valid, error_message = await self.verify_lean_proof(
+ theorem_header=theorem_header,
+ proof_completion=cleaned_proof_steps,
+ timeout_seconds=self.config.proof_verification_timeout_seconds,
+ )
+ score = 1.0 if is_valid else 0.0
+ scores.append(score)
+ completions_data.append(
+ {
+ "completion": cleaned_proof_steps,
+ "raw_llm_output": raw_llm_output,
+ "score": score,
+ "error_message": error_message if not is_valid else "",
+ }
+ )
+ current_messages = [
+ f"{msg_dict['role'].capitalize()}:\n\n{msg_dict['content']}"
+ for msg_dict in prompt_messages
+ ]
+ current_messages.append(f"Assistant:\n\n{cleaned_proof_steps}")
+ processed_messages_for_html.append("\n\n---\n\n".join(current_messages))
+ try:
+ tokens = (
+ self.tokenizer.encode(prompt_text_for_tokenizer)
+ if hasattr(self, "tokenizer") and self.tokenizer
+ else []
+ )
+ masks = [1] * len(tokens) if tokens else []
+ except Exception as e:
+ print(f"Warning: Tokenization failed - {e}. Using empty tokens/masks.")
+ tokens, masks = [], []
+ scored_data = ScoredDataGroup(
+ item_uuid=item["item_uuid"],
+ messages=processed_messages_for_html,
+ scores=scores,
+ tokens=[tokens] * len(llm_raw_outputs),
+ masks=[masks] * len(llm_raw_outputs),
+ metadata=[
+ {
+ "problem_name": problem_data["name"],
+ "attempt": i,
+ "error": comp_data["error_message"],
+ }
+ for i, comp_data in enumerate(completions_data)
+ ],
+ )
+ step_details = []
+ for i, comp_data in enumerate(completions_data):
+ step_details.append(
+ {
+ "problem_name": problem_data["name"],
+ "prompt": prompt_messages,
+ "raw_llm_output": comp_data["raw_llm_output"],
+ "cleaned_proof": comp_data["completion"],
+ "score": comp_data["score"],
+ "error_message": comp_data["error_message"],
+ "is_valid": comp_data["score"] == 1.0,
+ }
+ )
+ if self.wandb_available and wandb:
+ if (
+ self.rollouts_table
+ and hasattr(self.config, "num_rollouts_to_keep")
+ and self.config.num_rollouts_to_keep > 0
+ ):
+ for detail in step_details:
+ self.rollouts_table.add_data(
+ item["item_uuid"],
+ problem_data["name"],
+ str(detail["prompt"]),
+ detail["cleaned_proof"],
+ detail["score"],
+ detail["error_message"],
+ )
+ else:
+ for detail in step_details:
+ print(
+ f"Problem: {detail['problem_name']}, Valid: {detail['is_valid']}, "
+ f"Score: {detail['score']}, Proof: {repr(detail['cleaned_proof'])}, "
+ f"Error: {detail['error_message']}"
+ )
+ return scored_data, step_details
+
+
+if __name__ == "__main__":
+ LeanProofEnv.cli()
diff --git a/environments/community/lean_proof_env/requirements.txt b/environments/community/lean_proof_env/requirements.txt
new file mode 100644
index 00000000..8bad124c
--- /dev/null
+++ b/environments/community/lean_proof_env/requirements.txt
@@ -0,0 +1,4 @@
+datasets
+wandb
+tqdm
+python-dotenv
diff --git a/environments/community/metric_card_generator/README.md b/environments/community/metric_card_generator/README.md
new file mode 100644
index 00000000..f26ffe8a
--- /dev/null
+++ b/environments/community/metric_card_generator/README.md
@@ -0,0 +1,65 @@
+# Metric Card Generator Environment
+
+## Design and Motivation
+
+This environment generates structured JSON configurations for Metric Card UI components for AI model evaluation dashboards. It demonstrates a closed-loop generation, evaluation, and visualization pipeline using Atropos.
+
+The environment challenges language models to produce well-structured, valid JSON metric card configurations that can be directly used in front-end applications. This tests the model's ability to:
+- Follow specific schema requirements
+- Generate complex nested structures
+- Maintain consistent JSON formatting
+- Create semantically meaningful metric descriptions
+
+## Quickstart
+
+```bash
+# Install dependencies
+pip install -r requirements.txt
+
+# Run the environment with process command to generate rollouts
+python metric_card_generator.py process --env.data_path_to_save_groups artifacts/metric_rollouts.jsonl
+
+# View the generated HTML visualization
+# Open artifacts/metric_rollouts.html in a browser
+```
+
+## Environment Components
+
+- **metric_card_generator.py**: Main environment implementation with prompting and evaluation logic
+- **extract_metric_training.py**: Utility to extract high-quality examples for training
+- **trainingDataScript.py**: Creates training datasets from collected examples
+- **show_score_distribution.py**: Visualization tool for analyzing model performance
+
+## Artifacts
+
+The artifacts folder includes:
+- **metric_rollouts.jsonl**: Raw model outputs with scores
+- **metric_rollouts.html**: Visualization of model outputs and scores
+- **metric_training.jsonl**: Processed examples suitable for fine-tuning
+- **metric_training_high_quality.jsonl**: Filtered high-quality examples
+
+## Evaluation Metrics
+
+The environment evaluates model outputs on several dimensions:
+
+- **JSON Validity**: Whether the output is valid, parseable JSON
+- **Schema Compliance**: Whether the output follows the required structure
+- **Semantic Quality**: Whether the metrics described make sense for the given context
+- **Formatting**: Proper nesting, field types, and attribute consistency
+
+## WandB Integration
+
+Performance metrics are logged to Weights & Biases, including:
+- Percent of valid JSON responses
+- Average scores across evaluation criteria
+- Token usage efficiency
+- Examples of best and worst performing generations
+
+## Use with Training
+
+This environment can be integrated into the Atropos training loop to improve a model's ability to generate structured JSON output:
+
+```bash
+# Example training command
+python example_trainer/trainer.py --environment metric_card_generator --model your_model --iterations 1000
+```
diff --git a/environments/community/metric_card_generator/artifacts/metric_rollouts.html b/environments/community/metric_card_generator/artifacts/metric_rollouts.html
new file mode 100644
index 00000000..fcddfd4a
--- /dev/null
+++ b/environments/community/metric_card_generator/artifacts/metric_rollouts.html
@@ -0,0 +1,2517 @@
+
+
+
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing conversion rate during December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer support tickets statistics for our e-commerce business for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing operational costs statistics for our energy business for Q2 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays session duration for our hospitality platform for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing churn rate during September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays bounce rate for our healthcare platform for Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a healthcare dashboard showing customer satisfaction during Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for subscription renewals in energy for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a technology dashboard showing conversion rate during August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer acquisition cost for our telecommunications platform for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays net promoter score for our education platform for this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a social media dashboard showing website traffic during Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a hospitality dashboard showing customer satisfaction during September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing return on investment statistics for our energy business for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Return on Investment (ROI)",
+ "value": "8.75%",
+ "description": "ROI for energy business in November",
+ "trend": {
+ "value": 1.2,
+ "isPositive": true
+ },
+ "icon": "chart-growth"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for operational costs in our education analytics for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays uptime percentage for our entertainment platform for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing sales growth during this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer satisfaction in our finance analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays session duration for our social media platform for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a entertainment dashboard showing delivery time during July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a transportation dashboard showing net promoter score during August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer acquisition cost in retail for Q2 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays conversion rate for our transportation platform for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer acquisition cost statistics for our telecommunications business for Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays profit margin for our retail platform for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for net promoter score in energy for May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer acquisition cost in our healthcare analytics for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for session duration in transportation for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing revenue statistics for our manufacturing business for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays inventory turnover for our energy platform for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer lifetime value statistics for our marketing business for Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a e-commerce dashboard showing profit margin during April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays profit margin for our agriculture platform for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for average order value in our healthcare analytics for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value",
+ "value": "$85",
+ "description": "Average value of orders in the last 24 hours",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "money"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer acquisition cost in entertainment for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for active users in our retail analytics for Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing churn rate during Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing return on investment statistics for our retail business for Q2 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for market share in our real estate analytics for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for click-through rate in our education analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing subscription renewals statistics for our technology business for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for market share in retail for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for uptime percentage in insurance for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for app downloads in retail for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer satisfaction in healthcare for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a healthcare dashboard showing conversion rate during February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a hospitality dashboard showing conversion rate during last 30 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for error rate in our insurance analytics for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays net promoter score for our transportation platform for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for app downloads in our marketing analytics for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a media dashboard showing session duration during this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for delivery time in our entertainment analytics for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays app downloads for our retail platform for this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a media dashboard showing active users during Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for average order value in insurance for March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value",
+ "value": "$1,250",
+ "description": "Average insurance order value for March",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "dollar"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing market share statistics for our real estate business for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays delivery time for our hospitality platform for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer acquisition cost for our finance platform for January.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing subscription renewals statistics for our telecommunications business for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a insurance dashboard showing error rate during October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing retention rate during Q2 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a energy dashboard showing bounce rate during November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays click-through rate for our media platform for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for churn rate in our agriculture analytics for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing active users statistics for our social media business for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing active users statistics for our finance business for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a social media dashboard showing customer support tickets during Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a telecommunications dashboard showing customer acquisition cost during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing operational costs statistics for our manufacturing business for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays revenue for our transportation platform for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a e-commerce dashboard showing return on investment during April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing click-through rate during January.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a hospitality dashboard showing average order value during September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value",
+ "value": "$85.40",
+ "description": "Average order value for September",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "chart-line"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a marketing dashboard showing retention rate during this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a media dashboard showing operational costs during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for bounce rate in e-commerce for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing employee productivity statistics for our hospitality business for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a finance dashboard showing uptime percentage during June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays page views for our media platform for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer acquisition cost statistics for our energy business for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for market share in our education analytics for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for net promoter score in our telecommunications analytics for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for churn rate in our marketing analytics for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for uptime percentage in e-commerce for March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for active users in our technology analytics for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for engagement rate in our e-commerce analytics for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for active users in our real estate analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for delivery time in telecommunications for Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a insurance dashboard showing engagement rate during this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays market share for our e-commerce platform for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing inventory turnover statistics for our transportation business for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Inventory Turnover (Last 7 Days)",
+ "value": "3.2",
+ "description": "Average number of times inventory was sold and replaced over the last week",
+ "trend": {
+ "value": 5.0,
+ "isPositive": true
+ },
+ "icon": "truck"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays net promoter score for our finance platform for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a e-commerce dashboard showing delivery time during February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a energy dashboard showing operational costs during last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for conversion rate in insurance for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for operational costs in our hospitality analytics for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for revenue in our hospitality analytics for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing delivery time statistics for our transportation business for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing website traffic statistics for our media business for Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a retail dashboard showing customer satisfaction during February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays conversion rate for our technology platform for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for inventory turnover in transportation for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a finance dashboard showing customer acquisition cost during May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing retention rate statistics for our technology business for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for bounce rate in our retail analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for uptime percentage in telecommunications for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for average order value in our real estate analytics for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer satisfaction in healthcare for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing churn rate statistics for our social media business for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a retail dashboard showing profit margin during May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a media dashboard showing engagement rate during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays employee productivity for our social media platform for January.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for market share in marketing for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for active users in retail for this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for active users in our insurance analytics for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Active Users (Last 7 Days)",
+ "value": "8,432",
+ "description": "Number of users active in the past week",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "users"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for employee productivity in agriculture for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Employee Productivity (Last 7 Days)",
+ "value": "85%",
+ "description": "Average productivity of employees in agriculture over the past week",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "plant"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for churn rate in insurance for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays bounce rate for our media platform for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing user growth statistics for our real estate business for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a technology dashboard showing click-through rate during Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing subscription renewals during last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for profit margin in education for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays user growth for our e-commerce platform for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for employee productivity in our manufacturing analytics for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for inventory turnover in our entertainment analytics for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Inventory Turnover (June)",
+ "value": "8.4",
+ "description": "Average times inventory was sold and replaced in June",
+ "trend": {
+ "value": 5.2,
+ "isPositive": true
+ },
+ "icon": "sync"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for session duration in our transportation analytics for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing uptime percentage statistics for our retail business for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing delivery time statistics for our transportation business for Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for app downloads in our media analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for churn rate in our insurance analytics for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing churn rate statistics for our media business for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing website traffic statistics for our manufacturing business for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for subscription renewals in our real estate analytics for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for sales growth in insurance for March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer acquisition cost statistics for our telecommunications business for Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for operational costs in education for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer support tickets in our technology analytics for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for average order value in our social media analytics for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing delivery time statistics for our hospitality business for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays net promoter score for our finance platform for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays session duration for our marketing platform for May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for net promoter score in retail for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing return on investment statistics for our telecommunications business for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer acquisition cost in agriculture for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Customer Acquisition Cost",
+ "value": "$45,000",
+ "description": "Cost to acquire a new customer in agriculture this quarter",
+ "trend": {
+ "value": -5.2,
+ "isPositive": false
+ },
+ "icon": "wallet",
+ "className": "agriculture-metric"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays sales growth for our real estate platform for this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays retention rate for our transportation platform for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer acquisition cost in our energy analytics for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a insurance dashboard showing click-through rate during Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays app downloads for our technology platform for Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing engagement rate statistics for our transportation business for Q2 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing market share statistics for our marketing business for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing engagement rate during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a transportation dashboard showing employee productivity during September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for uptime percentage in our entertainment analytics for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for error rate in social media for March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Error Rate",
+ "value": "2.3%",
+ "description": "Error rate for social media in March",
+ "trend": {
+ "value": -0.5,
+ "isPositive": false
+ },
+ "icon": "error"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays uptime percentage for our manufacturing platform for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer acquisition cost for our education platform for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a transportation dashboard showing engagement rate during Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer satisfaction in our media analytics for May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for uptime percentage in our education analytics for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays conversion rate for our energy platform for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for average order value in our energy analytics for May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value",
+ "value": "$75.50",
+ "description": "Average order value for energy sales in May",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "dollar"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for app downloads in insurance for Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays delivery time for our finance platform for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing return on investment statistics for our media business for Q2 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Return on Investment (ROI) Q2 2023",
+ "value": "125%",
+ "description": "ROI for media business in Q2 2023",
+ "trend": {
+ "value": 8.4,
+ "isPositive": true
+ },
+ "icon": "trending-up"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer acquisition cost for our hospitality platform for Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing conversion rate statistics for our e-commerce business for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a marketing dashboard showing website traffic during last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays session duration for our retail platform for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a energy dashboard showing revenue during April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for error rate in our technology analytics for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing page views statistics for our education business for Q1 2024.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a manufacturing dashboard showing user growth during July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for profit margin in our healthcare analytics for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer satisfaction for our healthcare platform for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for market share in our marketing analytics for May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for website traffic in our manufacturing analytics for January.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing churn rate statistics for our technology business for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for average order value in technology for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing revenue statistics for our transportation business for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a hospitality dashboard showing session duration during February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a manufacturing dashboard showing uptime percentage during May.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for average order value in our finance analytics for Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value",
+ "value": "$1,250.75",
+ "description": "Average order value for Q3 2023",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "dollar"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for customer lifetime value in our media analytics for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing retention rate statistics for our retail business for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays retention rate for our insurance platform for Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for user growth in finance for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a healthcare dashboard showing click-through rate during July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer support tickets in transportation for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for delivery time in our hospitality analytics for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing churn rate statistics for our finance business for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for uptime percentage in insurance for this month.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer satisfaction statistics for our technology business for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for bounce rate in our energy analytics for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for churn rate in marketing for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for error rate in insurance for last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a telecommunications dashboard showing operational costs during last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing engagement rate statistics for our social media business for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a insurance dashboard showing market share during August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for engagement rate in our hospitality analytics for Q4 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing subscription renewals statistics for our retail business for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing user growth statistics for our e-commerce business for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays customer satisfaction for our real estate platform for August.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a agriculture dashboard showing user growth during last 30 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for engagement rate in our entertainment analytics for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing customer acquisition cost statistics for our real estate business for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for engagement rate in our entertainment analytics for Q2 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for active users in real estate for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Active Users (July)",
+ "value": "1,245",
+ "description": "Number of active users on real estate platform in July",
+ "trend": {
+ "value": 8.2,
+ "isPositive": true
+ },
+ "icon": "users"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays revenue for our insurance platform for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays app downloads for our hospitality platform for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for error rate in our insurance analytics for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for revenue in our e-commerce analytics for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing conversion rate statistics for our manufacturing business for September.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a hospitality dashboard showing customer satisfaction during last 24 hours.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays click-through rate for our manufacturing platform for this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a real estate dashboard showing return on investment during this quarter.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Return on Investment",
+ "value": "8.75%",
+ "description": "ROI for the current quarter",
+ "trend": {
+ "value": 1.2,
+ "isPositive": true
+ },
+ "icon": "trending-up"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a telecommunications dashboard showing conversion rate during Q3 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for active users in transportation for last 7 days.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Design a metric component that displays page views for our finance platform for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing retention rate statistics for our healthcare business for November.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer lifetime value in entertainment for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for delivery time in agriculture for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing delivery time statistics for our media business for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for uptime percentage in real estate for Q2 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
I need a metric card showing active users statistics for our healthcare business for October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a insurance dashboard showing operational costs during Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for session duration in our retail analytics for April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for subscription renewals in manufacturing for June.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for churn rate in media for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for average order value in transportation for February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Average Order Value (Transportation - February)",
+ "value": "$1,250",
+ "description": "Average order value in transportation for February",
+ "trend": {
+ "value": 3.2,
+ "isPositive": true
+ },
+ "icon": "shopping-cart"
+ }
+}<|eot_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a entertainment dashboard showing active users during October.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for sales growth in finance for year-to-date.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a transportation dashboard showing app downloads during Q1 2023.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Provide a metric card JSON for active users in our telecommunications analytics for July.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a energy dashboard showing market share during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a technology dashboard showing page views during April.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a e-commerce dashboard showing error rate during March.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for return on investment in e-commerce for December.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create a metric card for customer support tickets in entertainment for this year.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate a JSON output for a marketing dashboard showing click-through rate during February.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
You are a deep thinking AI, specialized in generating detailed use cases. You may use extremely long chains of thought to deeply consider the problem and deliberate with yourself via systematic reasoning processes to come up with comprehensive use cases. You should enclose your thoughts and internal monologue inside tags, and then provide your solution or response to the problem.
+
You are allocated a maximum of 2048 tokens, please strive to use less.
+
Your task is to generate detailed use cases based on the provided description. Each use case should include:
+1. A descriptive title
+2. User goals and objectives
+3. Step-by-step flow of actions
+4. Expected outcomes
+5. Edge cases or alternative paths
+
Format each use case clearly with appropriate headers and structure.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Generate detailed use cases for a mobile banking application that focuses on security features, user-friendly interface, and innovative payment solutions.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
+This task involves creating comprehensive use cases for a mobile banking app with three core focuses: security features, user-friendly interface, and innovative payment solutions. The goal is to produce detailed, systematic, and practical scenarios that can guide development, testing, and user training.
+
I'll approach this by identifying core functionalities typical of such an app, then elaborating on security features, usability aspects, and advanced payment options. I'll ensure each use case includes title, user goals, step-by-step flow, expected outcomes, and alternative paths.
+
To keep the volume within tokens, I will design several core use cases covering different aspects:
+
+
User registration and biometric authentication
+
Secure login with multi-factor authentication
+
Enabling/Disabling security features
+
Transferring funds via innovative payment methods (e.g., QR code, contactless)
+
Making payments using biometric verification
+
Detecting and preventing fraud
+
User-friendly onboarding and navigation
+
Customizing security and payment preferences
+
+
I will first generate about 3-4 detailed use cases, then, if needed, expand further.
+
+
Use Case 1: User Registration with Security Features
+
Title: User Registration and Account Setup with Security Initialization
+
Goals and Objectives:
+- Enable new users to register securely.
+- Collect user identity information.
+- Set up multi-factor authentication (e.g., biometric, PIN).
+- Ensure secure transmission and storage of sensitive data.
+
Flow of Actions:
+1. User downloads and opens the app.
+2. User enters personal information: name, email, phone number.
+3. User creates a strong password.
+4. App sends verification code via email/SMS for contact verification.
+5. User enters received verification code.
+6. User is prompted to set up biometric authentication (fingerprint/face) if available.
+7. User can opt to set up a PIN code for fallback.
+8. App securely encrypts and stores authentication credentials.
+9. User completes registration; the app displays a friendly onboarding tutorial emphasizing security features.
+
Expected Outcomes:
+- Successful account creation with verified contact info.
+- Biometric and PIN authentication options enabled.
+- Data securely stored with encryption.
+- User guided seamlessly through onboarding.
+
Edge Cases / Alternatives:
+- User enters incorrect verification code; app prompts retry.
+- Device doesn't support biometric; user proceeds with PIN.
+- Network failure during registration; app allows retry.
+
+
Use Case 2: Secure Login with Multi-Factor Authentication
+
Title: Secure User Login Incorporating Multi-Factor Authentication
+
Goals and Objectives:
+- Authenticate user securely.
+- Prevent unauthorized access.
+- Allow quick login via biometrics, with fallback to PIN/password.
+
Flow of Actions:
+1. User opens the app and initiates login.
+2. User enters username or email.
+3. User inputs password.
+4. App prompts for second factor:
+ - Option 1: Biometric verification (if registered and available).
+ - Option 2: One-time code sent via SMS/email.
+ - Option 3: Pre-set PIN.
+5. User completes second factor verification.
+6. App grants access and displays the dashboard.
+
Expected Outcomes:
+- User successfully logs in.
+- Security is reinforced through multi-factor processes.
+- Session tokens are securely generated and stored.
+
Edge Cases / Alternatives:
+- Biometric verification fails or unavailable; fallback to code/PIN.
+- User enters wrong password or code; app retries or blocks after multiple failures.
+- Network issues prevent code delivery; prompt user to retry later.
+
+
Use Case 3: Secure and Innovative Payment via QR Code
+
Title: Making a Payment Using QR Code with Security Measures
+
Goals and Objectives:
+- Enable fast, contactless payments.
+- Ensure transaction security.
+- Use innovative payment methods (QR code scanning).
+
Flow of Actions:
+Payee side:
+1. Payee generates a payment QR code via the app showing amount and account info.
+2. QR code is displayed on the screen or printed.
+
Payer side:
+3. User opens the app and selects ‘Scan QR code’ feature.
+4. App activates camera; user scans the QR code.
+5. App verifies QR code data against security parameters:
+ - Checks authenticity and expiry time.
+ - Confirms sufficient balance/security flags.
+6. User verifies the transaction amount and recipient.
+7. User confirms payment via biometric authentication or PIN.
+8. App processes the payment securely.
+9. Confirmation is displayed; transaction details are stored in history.
+
Expected Outcomes:
+- Contactless, quick payment with cryptographic validation.
+- User's identity verified securely before completion.
+- Transaction recorded properly.
+
Edge Cases / Alternatives:
+- QR code invalid or expired; app prompts user to retry.
+- Insufficient balance; app notifies user.
+- Camera access issues; fallback to manual input of recipient info (if supported).
+
+
Use Case 4: Fraud Detection and Alert
+
Title: Real-Time Fraud Detection and User Alert
+
Goals and Objectives:
+- Monitor suspicious activities.
+- Alert user proactively.
+- Enable quick response: lock account or verify activity.
+
Flow of Actions:
+1. The app continuously monitors transactions using pattern analysis.
+2. If suspicious activity is detected (e.g., large transfer, unusual device/location):
+ - App automatically flags event.
+3. User receives immediate notification (push/email/SMS).
+4. Notification prompts user to verify recent activity.
+5. User confirms or denies transaction.
+6. If confirmed suspicious, user can lock account or initiate security lock.
+7. App logs the event and alerts security team if necessary.
+
Expected Outcomes:
+- Fraudulent transactions are mitigated.
+- Users are informed promptly about suspicious activity.
+- Account integrity maintained.
+
Edge Cases / Alternatives:
+- False-positive alerts; user reports activity as legitimate.
+- User is unaware or unreachable; app auto-locks account temporarily.
+- Multiple alerts in short time; escalation protocols activate.
+
+
+This provides a strong foundation covering core security, usability, and innovative payment features. These use cases are modular and can be expanded to include scenarios like account recovery, personalized security settings, or advanced payment features like contactless NFC payments. For their clarity and detail, they should serve well for development and testing documentation.
+<|eot_id|>
You are a deep thinking AI, specialized in generating detailed use cases. You may use extremely long chains of thought to deeply consider the problem and deliberate with yourself via systematic reasoning processes to come up with comprehensive use cases. You should enclose your thoughts and internal monologue inside tags, and then provide your solution or response to the problem.
+
You are allocated a maximum of 2048 tokens, please strive to use less.
+
Your task is to generate detailed use cases based on the provided description. Each use case should include:
+1. A descriptive title
+2. User goals and objectives
+3. Step-by-step flow of actions
+4. Expected outcomes
+5. Edge cases or alternative paths
+
Format each use case clearly with appropriate headers and structure.<|eot_id|><|start_header_id|>user<|end_header_id|>
+
Create comprehensive use cases for a mobile banking system with emphasis on chat support, innovative payment solutions, and voice control.<|eot_id|><|start_header_id|>assistant<|end_header_id|>
+
+Alright, to generate comprehensive use cases for a mobile banking system emphasizing chat support, innovative payment solutions, and voice control, I must consider various user interactions, system responses, and edge cases. The use cases should encompass typical scenarios, alternative flows, and exceptional situations to ensure robustness.
+
I'll structure each use case with a title, user goals, step-by-step actions, expected outcomes, and potential edge cases. Given the three focus areas—chat support, innovative payment solutions, and voice control—I will develop separate use cases for each domain, ensuring coverage of key features.
+
Let's start by identifying core functionalities:
+
+
Chat support:
+
User inquiries (e.g., account info, transaction disputes, FAQs)
+
Live agent escalation
+
Innovative payment solutions:
+
Peer-to-peer payments
+
Contactless/QR code payments
+
Scheduled and recurring payments
+
Integration with digital wallets
+
Voice control:
+
Balance inquiry via voice
+
Initiating payments verbally
+
Transaction status updates
+
Authentication via voice
+
+
Now, onto detailed use cases:
+
+
Use Case 1: Chat Support - Resolving User Inquiry About Recent Transaction
+
Title: User Queries Recent Transaction via Chat Support
+
User Goals & Objectives:
+- Obtain detailed information about a recent transaction
+- Clarify any discrepancies or errors
+- Get guidance on dispute process if needed
+
Step-by-Step Flow:
+
+
User opens the mobile banking app and navigates to chat support.
+
User initiates a chat session and types: "I want details about my last transaction."
+
System recognizes intent and presents recent transaction history.
+
User selects a specific transaction or types transaction details (e.g., date, amount).
+
System displays comprehensive details (merchant, date, amount, location).
+
User inquires about a discrepancy: "Why was I charged twice?"
+
System prompts user to specify which transaction to dispute.
+
User confirms and requests to dispute the transaction.
+
System guides user through dispute process or escalates to a live agent if needed.
+
User receives confirmation of dispute initiation, with estimated resolution time.
+
+
Expected Outcomes:
+- User successfully retrieves transaction info.
+- Dispute is recorded and processed.
+- Support interaction is logged for future reference.
+
Edge Cases / Alternative Paths:
+- User's transaction not found (older transaction, incorrect details). System suggests alternative search criteria or escalates to live agent.
+- Chatbot cannot understand user's query. Option to escalate to human support.
+- User disconnects during the process; system saves chat context for future.
+
+
Use Case 2: Innovative Payment - Peer-to-Peer Money Transfer Using QR Code
+
Title: User Sends Money to Friend via QR Code
+
User Goals & Objectives:
+- Quickly transfer funds to a trusted contact
+- Use contactless method for convenience and security
+
Step-by-Step Flow:
+
+
User logs into the mobile banking app.
+
User navigates to "Transfer" > "Send via QR Code."
+
System displays QR code representing user’s payment info.
+
Friend opens their app, scans the QR code.
+
System verifies the recipient's identity and confirms the amount (or prompts user to specify amount).
+
User enters the transfer amount, reviews details.
+
User authorizes the transaction via biometric authentication or PIN.
+
System processes payment, updates both accounts, and sends confirmation notifications.
+
Both users receive transaction confirmation via app and optional email/SMS.
+
+
Expected Outcomes:
+- Funds are transferred securely and promptly.
+- Transaction is reflected in both users’ account histories.
+
Edge Cases / Alternative Paths:
+- QR code expired or invalid (e.g., scanned after expiry). System prompts to generate a new code.
+- Insufficient funds in sender's account. System notifies and offers options.
+- User cancels before completing payment.
+- Connectivity issues during scanning or processing.
+
+
Use Case 3: Voice Control - Checking Account Balance with Voice Command
+
Title: User Checks Account Balance Using Voice Control
+
User Goals & Objectives:
+- Obtain account balance hands-free
+- Quickly access financial info without manual navigation
+
Step-by-Step Flow:
+
+
User activates voice assistant (e.g., "Hey Banking") or presses voice control button.
+
User states: "What is my current balance?"
+
Voice system authenticates user via voice recognition or prompts for PIN/Biometric verification.
+
System verifies identity.
+
System retrieves latest account balance data.
+
System responds verbally: "Your current balance is $3,250."
+
User may ask follow-up questions, e.g., "Show recent transactions," which are read aloud or displayed on screen if visual confirmation is enabled.
+
+
Expected Outcomes:
+- User obtains accurate balance info securely.
+- The process is seamless, natural, and quick.
+
Edge Cases / Alternative Paths:
+- Voice recognition fails; system prompts for manual authentication.
+- App does not recognize command or misinterprets; prompts user to repeat.
+- Network issues delaying data retrieval; user advised to try later.
+
+
Use Case 4: Voice Control - Initiating a Scheduled Payment via Voice Command
+
Title: User Schedules a Bill Payment Using Voice Command
+
User Goals & Objectives:
+- Set up a recurring bill payment without manual entry
+- Use voice for convenience and efficiency
+
Step-by-Step Flow:
+
+
User activates voice command feature.
+
User says: "Schedule a monthly payment of $200 to electricity company starting next Monday."
+
System confirms understanding: "You want to set a recurring payment of $200 to Electricity Co. starting next Monday, each month. Is that correct?"
+
User confirms via voice or touch.
+
System prompts for authentication if not already verified.
+
Upon successful verification, system schedules the payment and confirms: "Your recurring payment has been scheduled."
+
User can ask: "Show my scheduled payments."
+
System lists upcoming scheduled payments.
+
+
Expected Outcomes:
+- Recurring payment is configured and stored securely.
+- User can manage or cancel via voice commands later.
+
Edge Cases / Alternative Paths:
+- Voice command misinterpreted; system asks for clarification.
+- Payment amount or date ambiguous; system requests clarification.
+- Authentication fails; prompts for manual login.
+- Network or system errors during scheduling.
+
+
Use Case 5: Multi-Modal Support - Combining Chat and Voice for Fraud Alert Resolution
+
Title: User Reports Suspicious Activity via Voice and Confirms Details via Chat
+
User Goals & Objectives:
+- Rapidly report fraudulent activity.
+- Use multi-modal inputs for clarity and confirmation.
+
Step-by-Step Flow:
+
+
User notices suspicious activity; activates voice assistant: "Report unauthorized transaction."
+
System confirms: "Please confirm, do you want to report a suspicious transaction?"
+
User confirms.
+
System gathers info: "Please describe the transaction or specify the date."
+
User reports details via voice.
+
System records info and offers to escalate: "Would you like to chat with a support agent for further assistance?"
+
User switches to chat support, where detailed info is reviewed, and support action is initiated.
+
User receives confirmation of report submission and next steps.
+
+
Expected Outcomes:
+- Fraud report is accurately logged.
+- User is guided through subsequent steps, including possible account freezes or further support.
+
Edge Cases / Alternative Paths:
+- Voice command not understood; user switches to chat.
+- User aborts the process.
+- Support team cannot verify details remotely; manual verification required.
+
+
+I've detailed use cases across the three focus areas, covering common actions, system responses, and alternative flows. These scenarios reflect real-world interactions emphasizing security, convenience, and innovative features. Each includes explicit steps, outcomes, and considerations for exceptions, making them suitable for comprehensive system design or testing documentation.
+
I can now compile these into a clean, concise list if needed, or expand further with additional scenarios. For now, this covers a broad, detailed set aligned with the prompt.<|eot_id|>
+
+
Reward: -0.03097743812904008
+
+
+
+
+
+
+
+
+
+
diff --git a/environments/community/metric_card_generator/extract_metric_training.py b/environments/community/metric_card_generator/extract_metric_training.py
new file mode 100644
index 00000000..f1079054
--- /dev/null
+++ b/environments/community/metric_card_generator/extract_metric_training.py
@@ -0,0 +1,104 @@
+import argparse
+import json
+import os
+import re
+
+
+def extract_training_examples(
+ input_file: str, output_file: str, score_threshold: float = -1.0
+):
+ """Extract training examples for metric cards from rollouts file"""
+
+ print(f"Extracting training examples from {input_file}")
+ print(f"Score threshold: {score_threshold}")
+
+ # Create output directory if it doesn't exist
+ output_dir = os.path.dirname(output_file)
+ if output_dir and not os.path.exists(output_dir):
+ os.makedirs(output_dir, exist_ok=True)
+
+ examples_kept = 0
+ examples_processed = 0
+
+ with open(input_file, "r") as f_in, open(output_file, "w") as f_out:
+ for line in f_in:
+ examples_processed += 1
+ try:
+ entry = json.loads(line)
+
+ # Check if we should keep this example based on score
+ if "scores" in entry and entry["scores"]:
+ best_score = max(entry["scores"])
+ if best_score <= score_threshold:
+ continue
+
+ # Extract messages string from the entry
+ if "messages" not in entry or not entry["messages"]:
+ continue
+
+ # The messages field is a string array containing raw message text
+ raw_messages = entry["messages"]
+ if not isinstance(raw_messages, str):
+ raw_messages = raw_messages[0] # Get first message if it's an array
+
+ # Extract system, user, and assistant parts using regex
+ system_match = re.search(
+ r"<\|start_header_id\|>system<\|end_header_id\|>\s*(.*?)<\|eot_id\|>",
+ raw_messages,
+ re.DOTALL,
+ )
+ user_match = re.search(
+ r"<\|start_header_id\|>user<\|end_header_id\|>\s*(.*?)<\|eot_id\|>",
+ raw_messages,
+ re.DOTALL,
+ )
+ assistant_match = re.search(
+ r"<\|start_header_id\|>assistant<\|end_header_id\|>\s*(.*?)<\|eot_id\|>",
+ raw_messages,
+ re.DOTALL,
+ )
+
+ if not system_match or not user_match or not assistant_match:
+ continue
+
+ system_content = system_match.group(1).strip()
+ user_content = user_match.group(1).strip()
+ assistant_content = assistant_match.group(1).strip()
+
+ # Combine system and user prompts
+ prompt = f"{system_content}\n\n{user_content}"
+
+ # Get the assistant's JSON response (it should already be in JSON format)
+ completion = assistant_content.strip()
+
+ # Write to output file in the format expected for fine-tuning
+ output_example = {"prompt": prompt, "completion": completion}
+
+ f_out.write(json.dumps(output_example, ensure_ascii=False) + "\n")
+ examples_kept += 1
+
+ except Exception as e:
+ print(f"Error processing line: {e}")
+
+ print(f"Processed {examples_processed} examples")
+ print(f"Kept {examples_kept} examples")
+
+ if examples_kept == 0:
+ print("\nNo examples were kept. Try lowering the score threshold.")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="Extract training examples from metric card rollouts"
+ )
+ parser.add_argument("input_file", help="Path to the input JSONL file with rollouts")
+ parser.add_argument("output_file", help="Path to the output training JSONL file")
+ parser.add_argument(
+ "--score_threshold",
+ type=float,
+ default=-1.0,
+ help="Minimum score to keep (default: -1.0 to keep all examples)",
+ )
+
+ args = parser.parse_args()
+ extract_training_examples(args.input_file, args.output_file, args.score_threshold)
diff --git a/environments/community/metric_card_generator/metric_card_generator.py b/environments/community/metric_card_generator/metric_card_generator.py
new file mode 100644
index 00000000..87a78884
--- /dev/null
+++ b/environments/community/metric_card_generator/metric_card_generator.py
@@ -0,0 +1,616 @@
+import json
+import os
+import random
+from typing import Dict, List, Optional, Tuple, TypedDict, Union
+
+from pydantic import BaseModel, ConfigDict, Field
+from tqdm.asyncio import tqdm_asyncio
+
+from atroposlib.envs.base import (
+ APIServerConfig,
+ BaseEnv,
+ BaseEnvConfig,
+ ScoredDataGroup,
+)
+from atroposlib.type_definitions import Item, number
+from atroposlib.utils.tokenize_for_trainer import tokenize_for_trainer
+
+# Example domains for generating metric queries
+DOMAINS = [
+ "e-commerce",
+ "finance",
+ "healthcare",
+ "education",
+ "real estate",
+ "social media",
+ "marketing",
+ "transportation",
+ "manufacturing",
+ "hospitality",
+ "technology",
+ "energy",
+ "retail",
+ "agriculture",
+ "media",
+ "telecommunications",
+ "insurance",
+ "entertainment",
+]
+
+# Metric type templates
+METRIC_TYPES = [
+ "revenue",
+ "user growth",
+ "conversion rate",
+ "customer acquisition cost",
+ "average order value",
+ "customer lifetime value",
+ "page views",
+ "bounce rate",
+ "churn rate",
+ "active users",
+ "session duration",
+ "retention rate",
+ "engagement rate",
+ "click-through rate",
+ "inventory turnover",
+ "profit margin",
+ "return on investment",
+ "customer satisfaction",
+ "net promoter score",
+ "sales growth",
+ "market share",
+ "employee productivity",
+ "website traffic",
+ "operational costs",
+ "customer support tickets",
+ "app downloads",
+ "subscription renewals",
+ "delivery time",
+ "error rate",
+ "uptime percentage",
+]
+
+# Time periods for metrics
+TIME_PERIODS = [
+ "this month",
+ "this quarter",
+ "this year",
+ "last 30 days",
+ "last 7 days",
+ "last 24 hours",
+ "year-to-date",
+ "Q1 2023",
+ "Q2 2023",
+ "Q3 2023",
+ "Q4 2023",
+ "Q1 2024",
+ "Q2 2024",
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+]
+
+# Icons for metric cards
+ICONS = [
+ "trending-up",
+ "trending-down",
+ "users",
+ "dollar",
+ "shopping-cart",
+ "bar-chart",
+ "percent",
+ "activity",
+ "alert-circle",
+ "clock",
+ "heart",
+ "globe",
+ "mail",
+ "phone",
+ "eye",
+ "tag",
+ "star",
+ "bell",
+ "download",
+ "upload",
+ "refresh",
+ "check-circle",
+ "shield",
+ "pie-chart",
+ "layers",
+ "calendar",
+ "thumbs-up",
+]
+
+# System prompt for metric card generation
+system_prompt = """You are an AI specialized in generating JSON data for a MetricCard React component.
+
+Your task is to create a JSON object following this structure:
+```json
+{
+ "componentType": "metricCard",
+ "props": {
+ "title": "Revenue",
+ "value": "$12,345",
+ "description": "Total revenue this month",
+ "trend": {
+ "value": 12.5,
+ "isPositive": true
+ },
+ "icon": "dollar"
+ }
+}
+```
+
+Required properties:
+- componentType: Must be exactly "metricCard"
+- props.title: The metric name/label
+- props.value: The metric value (formatted appropriately)
+
+Optional properties:
+- props.description: Additional context about the metric
+- props.trend: An object with 'value' (percentage) and 'isPositive' (boolean)
+- props.icon: Name of the icon (e.g., "dollar", "users", "trending-up")
+- props.className: CSS class name for custom styling
+
+IMPORTANT:
+1. Ensure the generated JSON is valid
+2. Format numeric values appropriately (add commas, currency symbols, or percentage signs as needed)
+3. Use appropriate icons based on the metric type
+4. Make sure trend values and descriptions are realistic
+
+You will receive a query describing what kind of metric is needed. Generate the appropriate JSON response.
+"""
+
+
+def generate_random_prompt():
+ """Generate a random prompt for metric card generation"""
+ domain = random.choice(DOMAINS)
+ metric_type = random.choice(METRIC_TYPES)
+ time_period = random.choice(TIME_PERIODS)
+
+ templates = [
+ f"Create a metric card for {metric_type} in {domain} for {time_period}.",
+ f"Generate a JSON output for a {domain} dashboard showing {metric_type} during {time_period}.",
+ f"I need a metric card showing {metric_type} statistics for our {domain} business for {time_period}.",
+ f"Design a metric component that displays {metric_type} for our {domain} platform for {time_period}.",
+ f"Provide a metric card JSON for {metric_type} in our {domain} analytics for {time_period}.",
+ ]
+
+ prompt = random.choice(templates)
+ return prompt, domain, metric_type, time_period
+
+
+# JSON schema for evaluating metric card output
+class TrendModel(BaseModel):
+ model_config = ConfigDict(extra="forbid", exclude_none=True)
+ value: float = Field(..., description="The percentage change value")
+ isPositive: bool = Field(
+ ..., description="Whether the trend is positive or negative"
+ )
+
+
+class MetricCardProps(BaseModel):
+ model_config = ConfigDict(extra="forbid", exclude_none=True)
+ title: str = Field(..., description="The metric name/label")
+ value: str = Field(
+ ..., description="The metric value (formatted as appropriate string)"
+ )
+ description: Optional[str] = Field(
+ None, description="Additional context about the metric"
+ )
+ trend: Optional[TrendModel] = Field(
+ None, description="Trend information including value and direction"
+ )
+ icon: Optional[str] = Field(None, description="Name of the icon to display")
+ className: Optional[str] = Field(
+ None, description="CSS class name for custom styling"
+ )
+
+
+class MetricCardComponent(BaseModel):
+ model_config = ConfigDict(extra="forbid", exclude_none=True)
+ componentType: str = Field(..., description="Must be exactly 'metricCard'")
+ props: MetricCardProps = Field(
+ ..., description="The properties for the metric card component"
+ )
+
+
+class PromptRow(TypedDict):
+ prompt: str
+ reference: str
+
+
+class MetricCardEnv(BaseEnv):
+ name = "metric_card_generator"
+
+ def __init__(
+ self,
+ config: BaseEnvConfig,
+ server_configs: List[APIServerConfig],
+ slurm=True,
+ testing=False,
+ ):
+ super().__init__(config, server_configs, slurm, testing)
+ self.percent_correct_buffer = list()
+ self.eval_metrics = list()
+ self.rollouts_for_wandb = []
+ self.completion_lengths = []
+ self.custom_prompt = None # Will be set dynamically
+ self.single_prompt_mode = True
+ self.output_file = "generated_metric_cards.json"
+ self.results = []
+ self.current_domains = []
+ self.current_metrics = []
+ self.current_periods = []
+ self.prompts_used = []
+
+ # Create output directory if it doesn't exist
+ os.makedirs("metric_card_outputs", exist_ok=True)
+
+ @classmethod
+ def config_init(cls) -> Tuple[BaseEnvConfig, List[APIServerConfig]]:
+ env_config = BaseEnvConfig(
+ tokenizer_name="NousResearch/DeepHermes-3-Llama-3-3B-Preview",
+ group_size=1,
+ use_wandb=True,
+ rollout_server_url="http://localhost:8000",
+ total_steps=1,
+ batch_size=1,
+ steps_per_eval=1,
+ max_token_length=1024,
+ wandb_name="metric_card_generator",
+ ensure_scores_are_not_same=False, # Disable score diversity check
+ )
+ server_configs = [
+ APIServerConfig(
+ model_name="NousResearch/DeepHermes-3-Llama-3-3B-Preview",
+ base_url="http://localhost:9001/v1",
+ api_key="x",
+ num_requests_for_eval=1,
+ ),
+ ]
+
+ return env_config, server_configs
+
+ async def wandb_log(self, wandb_metrics: Optional[Dict] = None):
+ if wandb_metrics is None:
+ wandb_metrics = {}
+
+ # Try to calculate percent_correct, pass if there's a division by zero
+ try:
+ wandb_metrics["train/percent_correct"] = sum(
+ self.percent_correct_buffer
+ ) / len(self.percent_correct_buffer)
+ except ZeroDivisionError:
+ # Skip if buffer is empty
+ pass
+
+ self.percent_correct_buffer = list()
+ for item in self.eval_metrics:
+ wandb_metrics[item[0]] = item[1]
+ self.eval_metrics = list()
+ # Call the parent method to handle the server metrics
+ await super().wandb_log(wandb_metrics)
+
+ async def setup(self, single_prompt=None):
+ # Generate a new prompt for this run
+ if not single_prompt:
+ prompt, domain, metric_type, time_period = generate_random_prompt()
+ self.custom_prompt = prompt
+ self.current_domains.append(domain)
+ self.current_metrics.append(metric_type)
+ self.current_periods.append(time_period)
+ else:
+ self.custom_prompt = single_prompt
+
+ self.prompts_used.append(self.custom_prompt)
+ self.single_prompt_mode = True
+ self.train = [{"prompt": self.custom_prompt, "reference": ""}]
+ self.test = []
+ self.iter = 0
+
+ print(f"\n=== USING PROMPT ===\n{self.custom_prompt}\n")
+
+ def save_checkpoint(self, step, data=None):
+ if data is None:
+ data = {}
+ data["iter"] = self.iter
+ super().save_checkpoint(step, data)
+
+ async def rollout_and_score_eval(self, prompt: str, reference: str = "") -> number:
+ _ = await self.server.chat_completion(
+ messages=[
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": prompt},
+ ],
+ n=1,
+ max_tokens=self.config.max_token_length,
+ temperature=0.0,
+ split="eval",
+ )
+
+ # In case of a reference, we could add a scoring mechanism here
+ # For metric cards, we'll just return 1 to indicate success
+ return 1
+
+ async def evaluate(self, *args, **kwargs):
+ if not self.test:
+ return
+
+ eval_tasks = []
+ for item in self.test:
+ eval_tasks.append(
+ self.rollout_and_score_eval(item["prompt"], item["reference"])
+ )
+ scores = await tqdm_asyncio.gather(*eval_tasks)
+ self.eval_metrics.append(("eval/percent_correct", sum(scores) / len(scores)))
+
+ async def evaluate_metric_card(self, json_text):
+ """Evaluate the quality of a metric card JSON response"""
+ # Initialize evaluation results
+ evaluation = {
+ "is_valid_json": False,
+ "has_required_fields": False,
+ "schema_valid": False,
+ "formatting_score": 0,
+ "overall_quality": 0,
+ "error": None,
+ }
+
+ # Check if it's valid JSON
+ try:
+ data = json.loads(json_text)
+ evaluation["is_valid_json"] = True
+
+ # Check if it has the required fields
+ if (
+ isinstance(data, dict)
+ and data.get("componentType") == "metricCard"
+ and "props" in data
+ and "title" in data["props"]
+ and "value" in data["props"]
+ ):
+ evaluation["has_required_fields"] = True
+
+ # Validate against schema
+ try:
+ _ = MetricCardComponent(**data)
+ evaluation["schema_valid"] = True
+
+ # Formatting score - evaluate how well values are formatted
+ props = data["props"]
+ formatting_score = 0
+
+ # Check value formatting
+ value = props["value"]
+ if isinstance(value, str):
+ # Check for currency formatting or percentage or number formatting
+ if (
+ ("$" in value or "€" in value or "£" in value)
+ or ("%" in value)
+ or ("," in value and any(c.isdigit() for c in value))
+ ):
+ formatting_score += 1
+
+ # Check trend formatting
+ if "trend" in props and isinstance(props["trend"], dict):
+ if isinstance(
+ props["trend"].get("value"), (int, float)
+ ) and isinstance(props["trend"].get("isPositive"), bool):
+ formatting_score += 1
+
+ # Check description format
+ if "description" in props and isinstance(props["description"], str):
+ if props["description"]: # Not empty
+ formatting_score += 1
+
+ # Normalize formatting score to 0-1
+ evaluation["formatting_score"] = min(formatting_score / 3, 1.0)
+
+ # Overall quality score (combining schema validity and formatting)
+ evaluation["overall_quality"] = (
+ evaluation["schema_valid"] * 0.5
+ + evaluation["formatting_score"] * 0.5
+ )
+
+ except Exception as e:
+ evaluation["error"] = f"Schema validation error: {str(e)}"
+
+ except Exception as e:
+ evaluation["error"] = f"JSON parsing error: {str(e)}"
+
+ return evaluation
+
+ async def collect_trajectories(
+ self, item: PromptRow
+ ) -> Tuple[ScoredDataGroup, list[Item]]:
+ user_message = {"role": "user", "content": item["prompt"]}
+
+ # Always use our current prompt
+ if not item["prompt"]:
+ user_message["content"] = self.custom_prompt
+
+ chat_completions = await self.server.chat_completion(
+ messages=[{"role": "system", "content": system_prompt}, user_message],
+ n=self.config.group_size,
+ max_tokens=self.config.max_token_length,
+ )
+ to_score = list()
+ to_backlog = list()
+
+ # Create a unique filename using the current step
+ step_count = len(self.results) + 1
+
+ for i, chat_completion in enumerate(chat_completions.choices):
+ messages = (
+ {"role": "system", "content": system_prompt},
+ user_message,
+ {"role": "assistant", "content": chat_completion.message.content},
+ )
+ to_score.append(
+ {
+ "messages": messages,
+ "reference": item.get("reference", ""),
+ "finish_reason": chat_completion.finish_reason,
+ }
+ )
+
+ # Get the generated response
+ response = chat_completion.message.content.strip()
+
+ # Try to extract JSON object if embedded in markdown (```json ... ```)
+ if "```json" in response and "```" in response.split("```json", 1)[1]:
+ json_text = response.split("```json", 1)[1].split("```", 1)[0].strip()
+ elif "```" in response and "```" in response.split("```", 1)[1]:
+ json_text = response.split("```", 1)[1].split("```", 1)[0].strip()
+ else:
+ json_text = response
+
+ # Evaluate the JSON
+ evaluation = await self.evaluate_metric_card(json_text)
+
+ # Create parsed version for JSON output if valid
+ parsed_json = {}
+ try:
+ if evaluation["is_valid_json"]:
+ parsed_json = json.loads(json_text)
+ except Exception:
+ parsed_json = {"error": "Could not parse JSON"}
+
+ # Log the generated metric card
+ print("\n=== GENERATED METRIC CARD ===\n")
+ print(json_text)
+ print("\n=== EVALUATION ===")
+ print(f"Valid JSON: {evaluation['is_valid_json']}")
+ print(f"Has Required Fields: {evaluation['has_required_fields']}")
+ print(f"Schema Valid: {evaluation['schema_valid']}")
+ print(f"Formatting Score: {evaluation['formatting_score']:.2f}")
+ print(f"Overall Quality: {evaluation['overall_quality']:.2f}")
+ if evaluation["error"]:
+ print(f"Error: {evaluation['error']}")
+ print("\n" + "-" * 50)
+
+ # Save current result with all metadata
+ current_result = {
+ "prompt": self.custom_prompt,
+ "raw_response": response,
+ "json_text": json_text,
+ "parsed_json": parsed_json,
+ "domain": self.current_domains[-1] if self.current_domains else "",
+ "metric_type": self.current_metrics[-1] if self.current_metrics else "",
+ "time_period": self.current_periods[-1] if self.current_periods else "",
+ "evaluation": evaluation,
+ "step": step_count,
+ "finish_reason": chat_completion.finish_reason,
+ }
+
+ self.results.append(current_result)
+
+ # Save individual result to a separate file
+ individual_file = f"metric_card_outputs/metric_card_{step_count}.json"
+ with open(individual_file, "w") as f:
+ json.dump(current_result, f, indent=2)
+
+ # Also save to the main output file
+ with open(self.output_file, "w") as f:
+ json.dump(
+ {
+ "results": self.results,
+ "prompts_used": self.prompts_used,
+ "domains": self.current_domains,
+ "metrics": self.current_metrics,
+ "time_periods": self.current_periods,
+ },
+ f,
+ indent=2,
+ )
+
+ # Create a dummy scored data group that will pass validation
+ scores = ScoredDataGroup()
+ scores["tokens"] = []
+ scores["masks"] = []
+ scores["scores"] = []
+
+ # Add some dummy data with different scores to pass the validation
+ for item in to_score:
+ out_dict = tokenize_for_trainer(
+ self.tokenizer, item["messages"], item["finish_reason"]
+ )
+ tokens = out_dict["tokens"]
+ masks = out_dict["masks"]
+
+ # Skip if tokenization failed
+ if len([1 for i in masks if i != -100]) < 10:
+ continue
+
+ # Add just enough token entries with different scores
+ scores["tokens"].append(tokens)
+ scores["masks"].append(masks)
+ scores["scores"].append(
+ random.uniform(-0.5, 0.5)
+ ) # Random scores to avoid the "all same" check
+
+ return scores, to_backlog
+
+ async def score(
+ self, rollout_group_data
+ ) -> Union[Optional[ScoredDataGroup], List[Optional[ScoredDataGroup]]]:
+ scores = ScoredDataGroup()
+ scores["tokens"] = list()
+ scores["masks"] = list()
+ scores["scores"] = list()
+
+ # Create artificially different scores to avoid the validation errors
+ for idx, item in enumerate(rollout_group_data):
+ out_dict = tokenize_for_trainer(
+ self.tokenizer, item["messages"], item["finish_reason"]
+ )
+ tokens = out_dict["tokens"]
+ masks = out_dict["masks"]
+
+ # Remove obviously bad examples
+ if len([1 for i in masks if i != -100]) < 10:
+ continue
+
+ scores["tokens"].append(tokens)
+ scores["masks"].append(masks)
+
+ # Assign different scores to ensure we pass validation
+ scores["scores"].append(
+ -1.0 + idx * 0.5
+ ) # Will give different scores for each item
+
+ # Ensure we have some scores
+ if len(scores["scores"]) == 0:
+ return None
+
+ return scores
+
+ async def get_next_item(self) -> PromptRow:
+ # Generate a new prompt for each step if we're in multi-step mode
+ if self.config.total_steps > 1:
+ prompt, domain, metric_type, time_period = generate_random_prompt()
+ self.custom_prompt = prompt
+ self.current_domains.append(domain)
+ self.current_metrics.append(metric_type)
+ self.current_periods.append(time_period)
+ self.prompts_used.append(prompt)
+ print(f"\n=== USING NEW PROMPT ===\n{prompt}\n")
+
+ # Return the current prompt
+ return {"prompt": self.custom_prompt, "reference": ""}
+
+
+# This is needed to use the CLI command with the existing framework
+if __name__ == "__main__":
+ MetricCardEnv.cli()
diff --git a/environments/community/metric_card_generator/requirements.txt b/environments/community/metric_card_generator/requirements.txt
new file mode 100644
index 00000000..470cb978
--- /dev/null
+++ b/environments/community/metric_card_generator/requirements.txt
@@ -0,0 +1,2 @@
+atroposlib
+requests
diff --git a/environments/community/metric_card_generator/show_score_distribution.py b/environments/community/metric_card_generator/show_score_distribution.py
new file mode 100644
index 00000000..973118d9
--- /dev/null
+++ b/environments/community/metric_card_generator/show_score_distribution.py
@@ -0,0 +1,78 @@
+import argparse
+import json
+
+
+def analyze_scores(input_file: str):
+ """Analyze and display the score distribution from a rollouts file"""
+
+ scores = []
+ with open(input_file, "r") as f:
+ for line_num, line in enumerate(f, 1):
+ try:
+ record = json.loads(line)
+ if "scores" in record and record["scores"]:
+ scores.extend(record["scores"])
+ except Exception as e:
+ print(f"Error on line {line_num}: {e}")
+
+ if not scores:
+ print("No scores found in the file.")
+ return
+
+ # Print summary statistics
+ print(f"Total scores: {len(scores)}")
+ print(f"Min score: {min(scores):.4f}")
+ print(f"Max score: {max(scores):.4f}")
+ print(f"Average score: {sum(scores)/len(scores):.4f}")
+
+ # Count scores in different ranges
+ ranges = {
+ "< 0.0": 0,
+ "0.0 to 0.1": 0,
+ "0.1 to 0.2": 0,
+ "0.2 to 0.3": 0,
+ "0.3 to 0.4": 0,
+ "0.4 to 0.5": 0,
+ "> 0.5": 0,
+ }
+
+ for score in scores:
+ if score < 0.0:
+ ranges["< 0.0"] += 1
+ elif score < 0.1:
+ ranges["0.0 to 0.1"] += 1
+ elif score < 0.2:
+ ranges["0.1 to 0.2"] += 1
+ elif score < 0.3:
+ ranges["0.2 to 0.3"] += 1
+ elif score < 0.4:
+ ranges["0.3 to 0.4"] += 1
+ elif score < 0.5:
+ ranges["0.4 to 0.5"] += 1
+ else:
+ ranges["> 0.5"] += 1
+
+ # Print score distribution
+ print("\nScore distribution:")
+ for range_name, count in ranges.items():
+ percentage = (count / len(scores)) * 100
+ print(f"{range_name}: {count} examples ({percentage:.1f}%)")
+
+ # Distribution for different threshold values
+ thresholds = [-0.5, -0.3, -0.1, 0.0, 0.1, 0.2, 0.3, 0.4, 0.5]
+ counts = []
+ for threshold in thresholds:
+ count = sum(1 for score in scores if score > threshold)
+ percentage = (count / len(scores)) * 100
+ counts.append(count)
+ print(f"Score > {threshold}: {count} examples ({percentage:.1f}%)")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="Analyze score distribution in a rollouts file"
+ )
+ parser.add_argument("input_file", help="Path to the input JSONL file with rollouts")
+
+ args = parser.parse_args()
+ analyze_scores(args.input_file)
diff --git a/environments/community/metric_card_generator/trainingDataScript.py b/environments/community/metric_card_generator/trainingDataScript.py
new file mode 100644
index 00000000..55ad9ff5
--- /dev/null
+++ b/environments/community/metric_card_generator/trainingDataScript.py
@@ -0,0 +1,342 @@
+import argparse
+import json
+import os
+
+
+def filter_and_format_for_finetune(
+ input_path: str, output_path: str, score_threshold: float = 0.0, debug: bool = False
+):
+ filtered_count = 0
+ total_count = 0
+ skipped_due_to_score = 0
+ skipped_due_to_structure = 0
+
+ # First, let's analyze the input file structure if in debug mode
+ if debug:
+ try:
+ with open(input_path, "r", encoding="utf-8") as infile:
+ first_line = infile.readline().strip()
+ print(f"First line of file (preview): {first_line[:100]}...")
+
+ # Try to parse it
+ try:
+ parsed = json.loads(first_line)
+ print(f"Keys in record: {list(parsed.keys())}")
+ if "scores" in parsed:
+ print(f"Scores: {parsed['scores']}")
+ if "tokens" in parsed:
+ print(f"Number of tokens: {len(parsed['tokens'])}")
+ if "messages" in parsed:
+ print(f"Number of messages: {len(parsed['messages'])}")
+ for i, msg in enumerate(parsed.get("messages", [])):
+ print(f"Message {i}: {str(msg)[:50]}...")
+ except Exception as e:
+ print(f"Error parsing first line: {e}")
+ except Exception as e:
+ print(f"Error reading file: {e}")
+
+ # Create output directory if it doesn't exist
+ os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
+
+ with (
+ open(input_path, "r", encoding="utf-8") as infile,
+ open(output_path, "w", encoding="utf-8") as outfile,
+ ):
+ for line_num, line in enumerate(infile, 1):
+ total_count += 1
+ try:
+ record = json.loads(line)
+
+ # Check if there are scores
+ if "scores" not in record or not record["scores"]:
+ if debug:
+ print(f"Line {line_num}: No scores found")
+ skipped_due_to_structure += 1
+ continue
+
+ # Get the best score (maximum value)
+ score = max(record.get("scores", [-float("inf")]))
+
+ if score <= score_threshold:
+ if debug:
+ print(
+ f"Line {line_num}: Score {score} is below threshold {score_threshold}"
+ )
+ skipped_due_to_score += 1
+ continue
+
+ # Check if we have the expected message structure
+ messages = []
+
+ # Check if we have raw messages or tokenized messages
+ if "messages" in record:
+ messages = record["messages"]
+ elif "tokens" in record and "masks" in record:
+ # Here we should have a way to detokenize, but for now we'll skip
+ if debug:
+ print(f"Line {line_num}: Has tokens but no messages")
+ skipped_due_to_structure += 1
+ continue
+
+ # Ensure we have enough messages
+ if len(messages) < 2: # Need at least system+user messages
+ if debug:
+ print(f"Line {line_num}: Not enough messages ({len(messages)})")
+ skipped_due_to_structure += 1
+ continue
+
+ # Try both message formats - array of objects or tuples
+ try:
+ if isinstance(messages[0], dict):
+ # Format where messages is array of objects with role/content
+ system_msg = next(
+ (m["content"] for m in messages if m["role"] == "system"),
+ "",
+ )
+ user_msg = next(
+ (m["content"] for m in messages if m["role"] == "user"), ""
+ )
+ assistant_msg = next(
+ (
+ m["content"]
+ for m in messages
+ if m["role"] == "assistant"
+ ),
+ "",
+ )
+ elif isinstance(messages, tuple) or (
+ isinstance(messages, list)
+ and len(messages) > 0
+ and isinstance(messages[0], tuple)
+ ):
+ # Format where messages are tuples
+ system_msg = (
+ messages[0]["content"]
+ if isinstance(messages[0], dict)
+ else messages[0][1]
+ )
+ user_msg = (
+ messages[1]["content"]
+ if isinstance(messages[1], dict)
+ else messages[1][1]
+ )
+ assistant_msg = (
+ messages[2]["content"]
+ if len(messages) > 2 and isinstance(messages[2], dict)
+ else messages[2][1] if len(messages) > 2 else ""
+ )
+ else:
+ if debug:
+ print(
+ f"Line {line_num}: Unsupported message format: {type(messages[0])}"
+ )
+ skipped_due_to_structure += 1
+ continue
+
+ # Construct prompt and completion
+ prompt = f"{system_msg.strip()}\n\n{user_msg.strip()}"
+ completion = assistant_msg.strip()
+
+ # Handle case where we have JSON response
+ # Extract only the JSON part if it's wrapped in explanation
+ if (
+ "```json" in completion
+ and "```" in completion.split("```json", 1)[1]
+ ):
+ completion = (
+ completion.split("```json", 1)[1].split("```", 1)[0].strip()
+ )
+ elif "```" in completion and "```" in completion.split("```", 1)[1]:
+ completion = (
+ completion.split("```", 1)[1].split("```", 1)[0].strip()
+ )
+
+ # Ensure the completion is valid JSON for our metric card
+ try:
+ completion_json = json.loads(completion)
+ # Verify it has the required structure for a metric card
+ if not (
+ isinstance(completion_json, dict)
+ and completion_json.get("componentType") == "metricCard"
+ and "props" in completion_json
+ and "title" in completion_json["props"]
+ and "value" in completion_json["props"]
+ ):
+ if debug:
+ print(f"Line {line_num}: Invalid metric card structure")
+ skipped_due_to_structure += 1
+ continue
+ except Exception as e:
+ if debug:
+ print(f"Line {line_num}: Completion is not valid JSON: {e}")
+ skipped_due_to_structure += 1
+ continue
+
+ # Write the formatted result
+ json.dump(
+ {"prompt": prompt, "completion": completion},
+ outfile,
+ ensure_ascii=False,
+ )
+ outfile.write("\n")
+ filtered_count += 1
+
+ if debug and filtered_count <= 5:
+ print(f"\nKept example {filtered_count}:")
+ print(f"PROMPT: {prompt[:100]}...")
+ print(f"COMPLETION: {completion[:100]}...")
+
+ except Exception as e:
+ if debug:
+ print(f"Line {line_num}: Error processing messages: {e}")
+ skipped_due_to_structure += 1
+ continue
+
+ except Exception as e:
+ print(f"Line {line_num}: Error processing record: {e}")
+ skipped_due_to_structure += 1
+
+ print(
+ f"Finished processing. Kept {filtered_count} out of {total_count} examples with score > {score_threshold}."
+ )
+ print(f"Skipped due to score: {skipped_due_to_score}")
+ print(f"Skipped due to structure: {skipped_due_to_structure}")
+
+ # If we didn't keep any examples but have data, recommend lowering the threshold
+ if filtered_count == 0 and total_count > 0:
+ print(
+ "\nRecommendation: No examples were kept. Try lowering the score threshold."
+ )
+ print(
+ "You can use --score_threshold -1.0 to see all examples regardless of score."
+ )
+
+ # If in debug mode, additionally show a histogram of scores
+ if debug:
+ try:
+ scores = []
+ with open(input_path, "r", encoding="utf-8") as infile:
+ for line in infile:
+ try:
+ record = json.loads(line)
+ if "scores" in record and record["scores"]:
+ scores.extend(record["scores"])
+ except Exception:
+ pass
+
+ if scores:
+ print("\nScore distribution:")
+ ranges = {
+ "-1.0 to -0.5": 0,
+ "-0.5 to 0.0": 0,
+ "0.0 to 0.5": 0,
+ "0.5 to 1.0": 0,
+ "Other": 0,
+ }
+
+ for score in scores:
+ if -1.0 <= score < -0.5:
+ ranges["-1.0 to -0.5"] += 1
+ elif -0.5 <= score < 0.0:
+ ranges["-0.5 to 0.0"] += 1
+ elif 0.0 <= score < 0.5:
+ ranges["0.0 to 0.5"] += 1
+ elif 0.5 <= score <= 1.0:
+ ranges["0.5 to 1.0"] += 1
+ else:
+ ranges["Other"] += 1
+
+ for range_name, count in ranges.items():
+ if count > 0:
+ print(
+ f"{range_name}: {count} examples ({count/len(scores)*100:.1f}%)"
+ )
+
+ print(f"Min score: {min(scores)}")
+ print(f"Max score: {max(scores)}")
+ print(f"Avg score: {sum(scores)/len(scores):.2f}")
+ except Exception as e:
+ print(f"Error analyzing score distribution: {e}")
+
+
+def analyze_raw_file(file_path: str):
+ """Analyzes a raw rollouts file to understand its structure"""
+ print(f"\nAnalyzing file: {file_path}")
+
+ try:
+ with open(file_path, "r", encoding="utf-8") as f:
+ lines = list(f)
+ print(f"File contains {len(lines)} lines")
+
+ if not lines:
+ print("File is empty")
+ return
+
+ # Analyze the first line
+ first_line = lines[0]
+ try:
+ data = json.loads(first_line)
+ print(f"First line has these keys: {list(data.keys())}")
+
+ if "results" in data:
+ print(
+ "This appears to be a consolidated results file, not a rollouts file"
+ )
+ print(f"It contains {len(data['results'])} results")
+
+ # Look at first result
+ if data["results"]:
+ first_result = data["results"][0]
+ print(f"First result keys: {list(first_result.keys())}")
+
+ if "json_text" in first_result:
+ print(
+ f"JSON text sample: {first_result['json_text'][:100]}..."
+ )
+
+ try:
+ json_obj = json.loads(first_result["json_text"])
+ print(
+ f"Valid JSON object with keys: {list(json_obj.keys())}"
+ )
+ except Exception:
+ print("JSON text is not valid JSON")
+
+ print("\nThis file is not in the expected format for the script.")
+ print(
+ "The script expects individual JSONL records, not a consolidated JSON file."
+ )
+ print("You may need to convert this file format first.")
+ return
+ except Exception as e:
+ print(f"Error parsing first line: {e}")
+ except Exception as e:
+ print(f"Error reading file: {e}")
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(
+ description="Filter and convert JSONL eval file for fine-tuning."
+ )
+ parser.add_argument("input_path", type=str, help="Path to the input JSONL file")
+ parser.add_argument("output_path", type=str, help="Path to the output JSONL file")
+ parser.add_argument(
+ "--score_threshold",
+ type=float,
+ default=0.0,
+ help="Minimum score to keep an example (default: 0.0)",
+ )
+ parser.add_argument("--debug", action="store_true", help="Enable debug output")
+ parser.add_argument(
+ "--analyze",
+ action="store_true",
+ help="Analyze file structure without processing",
+ )
+ args = parser.parse_args()
+
+ if args.analyze:
+ analyze_raw_file(args.input_path)
+ else:
+ filter_and_format_for_finetune(
+ args.input_path, args.output_path, args.score_threshold, args.debug
+ )
diff --git a/environments/community/philosophical_rlaif_env.py b/environments/community/philosophical_rlaif_env.py
new file mode 100644
index 00000000..04d98266
--- /dev/null
+++ b/environments/community/philosophical_rlaif_env.py
@@ -0,0 +1,744 @@
+import asyncio
+import random
+import re # Added import for regular expressions
+from typing import Dict, List, Optional, Tuple
+
+import wandb # type: ignore
+from datasets import load_dataset # type: ignore
+from pydantic import Field
+
+from atroposlib.envs.base import BaseEnvConfig # Ensure BaseEnvConfig is imported
+from atroposlib.envs.base import (
+ APIServerConfig,
+ BaseEnv,
+ EvalHandlingEnum,
+ ScoredDataGroup,
+)
+from atroposlib.utils.tokenize_for_trainer import tokenize_for_trainer
+
+# Judge system prompt can remain somewhat generic for deep thinking
+judge_system_prompt = (
+ "You are a deep thinking AI, you may use extremely long chains of thought "
+ "to deeply consider the problem and deliberate with yourself via systematic "
+ "reasoning processes to help come to a correct solution prior to answering. "
+ "You should enclose your thoughts and internal monologue inside "
+ "tags, and then provide your solution or response to the problem.\n\n"
+)
+judge_system_prompt += (
+ "You are allocated a maximum of 4096 tokens for your thoughts and response, "
+ "please strive to use less for thinking.\n\n"
+ "You will then provide your preferred response like this: \\boxed{A or B}.\n\n"
+ "It is important that you provide your answer in the correct format.\n"
+ "If you do not, you will not receive credit for your answer.\n\n"
+ "So please end your answer with \\boxed{A or B}"
+)
+
+# New preference string for philosophical depth
+philosophical_preference_string = (
+ "I prefer responses that demonstrate deep philosophical inquiry. The ideal response "
+ "explores multiple perspectives, questions underlying assumptions, uses nuanced language, "
+ "and encourages further critical thinking. Avoid superficial answers; aim for depth "
+ "and intellectual stimulation. Responses that identify underlying principles, paradoxes, "
+ "or ethical considerations are highly valued. Clarity and cogency in expressing complex "
+ "ideas are also important."
+)
+
+RLAIF_user_prompt_format_str = (
+ "[Conversation]\n{conversation}\n\n"
+ "[Response A]\n{response_a}\n\n"
+ "[Response B]\n{response_b}\n\n"
+ "[END]\n\n"
+ "Here's a conversation that has two responses. Please pick the one that is more aligned "
+ "with the following desired qualities for a response:\n{rl_preference_string}\n\n"
+ "The goal is to select the response that, if used for training an AI, would better steer "
+ "it towards generating text with these qualities, even if neither response is a perfect example.\n"
+ "Please do your best to evaluate which response better embodies or tends towards the "
+ "described philosophical depth and nuance.\n\n"
+ "Go ahead and think through it, then give me your answer with \\boxed{{A or B}}."
+)
+
+
+class PhilosophicalRLAIFConfig(
+ BaseEnvConfig
+): # Custom config if needed, inherits BaseEnvConfig
+ # Add any specific configurations here if needed in the future
+ # For now, we rely on BaseEnvConfig defaults and overrides in config_init
+ judge_model_name: str = Field(
+ default="gpt-3.5-turbo", description="Model to use for judging preferences."
+ )
+ generator_model_name: str = Field(
+ default="gpt-3.5-turbo",
+ description="Model to use for generating initial responses.",
+ )
+ judge_max_tokens: int = Field(
+ default=2048, description="Max tokens for judge response."
+ )
+ generator_max_tokens: int = Field(
+ default=1024, description="Max tokens for generator response."
+ )
+
+
+class PhilosophicalRLAIFEnv(BaseEnv):
+ name = "philosophical_rlaif"
+ env_config_cls = PhilosophicalRLAIFConfig
+
+ def __init__(
+ self,
+ config: PhilosophicalRLAIFConfig, # Use the new config
+ server_configs: List[APIServerConfig],
+ slurm=True,
+ testing=False,
+ ):
+ super().__init__(config, server_configs, slurm, testing)
+ self.eval_metrics = list() # Kept for consistency, though eval is basic
+ self.judgement_strings_buffer: List[Tuple[str, str, str]] = list()
+ self.preference_scores_buffer: List[float] = list()
+ self.train_dataset = None # Initialize attribute
+
+ @classmethod
+ def config_init(cls) -> Tuple[PhilosophicalRLAIFConfig, List[APIServerConfig]]:
+ env_config = PhilosophicalRLAIFConfig( # Use the new config class
+ tokenizer_name="cl100k_base", # Changed from gpt2
+ group_size=2,
+ use_wandb=True,
+ rollout_server_url="http://localhost:8000",
+ total_steps=20,
+ batch_size=4,
+ steps_per_eval=10,
+ max_token_length=4096, # Increased from 3072
+ score_buffer_size=4,
+ wandb_name="philosophical_rlaif_shortgen", # New wandb name for this attempt
+ eval_handling=EvalHandlingEnum.LIMIT_TRAIN,
+ eval_limit_ratio=0.1,
+ judge_model_name="gpt-3.5-turbo",
+ generator_model_name="gpt-3.5-turbo",
+ judge_max_tokens=1024, # Reduced as inputs will be shorter
+ generator_max_tokens=768, # Increased from 256
+ data_path_to_save_groups="./philosophical_rlaif_rollouts.jsonl",
+ ensure_scores_are_not_same=False, # More lenient for ties
+ )
+ # We'll use one server config, assuming generator and judge models are on the same API endpoint
+ # The actual model used for each call can be specified in the chat_completion call if needed,
+ # or we assume the server config's model_name is used if not overridden.
+ # For this example, we'll use the same model for both roles from the config.
+ server_configs = [
+ APIServerConfig(
+ model_name=env_config.judge_model_name, # Default model for the server
+ base_url=None, # Use OpenAI default
+ api_key=None, # Expect API key from environment (.env file)
+ num_requests_for_eval=32, # For potential eval calls
+ ),
+ ]
+ return env_config, server_configs
+
+ async def wandb_log(self, wandb_metrics: Optional[Dict] = None):
+ if wandb_metrics is None:
+ wandb_metrics = {}
+
+ if self.judgement_strings_buffer:
+ table = wandb.Table(columns=["resp_a", "resp_b", "sample_judgement"])
+ for item in self.judgement_strings_buffer:
+ table.add_data(item[0], item[1], item[2])
+ self.judgement_strings_buffer.clear()
+ wandb_metrics["train/judgement_table"] = table
+ print("Logged judgement table to W&B.")
+
+ if self.preference_scores_buffer:
+ avg_pref_score = sum(self.preference_scores_buffer) / len(
+ self.preference_scores_buffer
+ )
+ wandb_metrics["train/avg_normalized_preference_score"] = avg_pref_score
+ print(
+ f"Average normalized preference score for batch: {avg_pref_score:.3f} "
+ f"(over {len(self.preference_scores_buffer)} scores)"
+ )
+ self.preference_scores_buffer.clear()
+
+ # Log other eval metrics if any
+ for metric_name, metric_value in self.eval_metrics:
+ wandb_metrics[metric_name] = metric_value
+ self.eval_metrics = list() # Clear after logging
+
+ await super().wandb_log(wandb_metrics)
+
+ async def setup(self):
+ # Using a small subset for faster loading during tests.
+ # In a real scenario, use the full split or a larger subset.
+ try:
+ self.train_dataset = load_dataset(
+ "allenai/WildChat", split="train[:1000]"
+ ) # Smaller subset
+ self.iter = 0
+ print(
+ f"PhilosophicalRLAIFEnv initialized with {len(self.train_dataset)} "
+ "training examples from WildChat."
+ )
+ except Exception as e:
+ print(f"Error loading dataset: {e}")
+ self.train_dataset = [] # Ensure it's an empty list on failure
+ self.iter = 0
+
+ def save_checkpoint(self, step, data=None):
+ if data is None:
+ data = {}
+ data["iter"] = self.iter
+ super().save_checkpoint(step, data)
+
+ async def rollout_and_score_eval_item(self, eval_item) -> float:
+ # Simplified eval: generate two responses, have judge pick, score 1 if first is picked, 0 otherwise.
+ # This is a placeholder, proper RLAIF eval is more complex.
+ original_chat = [
+ dict(msg) for msg in eval_item if msg["role"] != "assistant"
+ ] # Get initial prompt
+ if not original_chat:
+ return 0.0
+
+ # Generate two responses (A and B)
+ completions = await self.server.chat_completion(
+ messages=original_chat,
+ n=2,
+ max_tokens=self.config.generator_max_tokens,
+ temperature=0.7,
+ model=self.config.generator_model_name, # Specify generator model
+ split="eval",
+ )
+ if len(completions.choices) < 2:
+ return 0.0 # Not enough responses to compare
+
+ response_a_content = completions.choices[0].message.content
+ response_b_content = completions.choices[1].message.content
+
+ conversation_str = "\n".join(
+ [f"{msg['role']}: {msg['content']}" for msg in original_chat]
+ )
+
+ judge_prompt_content = RLAIF_user_prompt_format_str.format(
+ conversation=conversation_str,
+ response_a=response_a_content,
+ response_b=response_b_content,
+ rl_preference_string=philosophical_preference_string,
+ )
+
+ judge_response = await self.server.chat_completion(
+ messages=[
+ {"role": "system", "content": judge_system_prompt},
+ {"role": "user", "content": judge_prompt_content},
+ ],
+ n=1,
+ max_tokens=self.config.judge_max_tokens,
+ temperature=0.0, # Judge should be deterministic
+ model=self.config.judge_model_name, # Specify judge model
+ split="eval",
+ )
+
+ chosen_val_match = re.search(
+ r"\boxed{(A|B)}", judge_response.choices[0].message.content
+ )
+ if chosen_val_match:
+ return (
+ 1.0 if chosen_val_match.group(1) == "A" else 0.0
+ ) # Arbitrary: score 1 if A is chosen
+ return 0.0 # No clear choice or format error
+
+ async def evaluate(self, *args, **kwargs):
+ if (
+ not self.train_dataset or len(self.train_dataset) == 0
+ ): # Use train_dataset for eval examples for simplicity
+ print("No evaluation data available (using subset of train_dataset).")
+ return
+
+ num_eval_samples = min(
+ 10, len(self.train_dataset)
+ ) # Evaluate on a small sample
+ eval_samples = random.sample(list(self.train_dataset), num_eval_samples)
+
+ print(
+ f"Evaluating on {num_eval_samples} samples from WildChat for philosophical preference..."
+ )
+ total_score = 0
+
+ for i, sample in enumerate(eval_samples):
+ print(f"Eval sample {i+1}/{num_eval_samples}")
+ # The 'conversation' field in WildChat is a list of dicts
+ eval_item_chat = sample.get("conversation", [])
+ if not eval_item_chat:
+ continue
+ score = await self.rollout_and_score_eval_item(eval_item_chat)
+ total_score += score
+
+ if num_eval_samples > 0:
+ avg_score = total_score / num_eval_samples
+ self.eval_metrics.append(
+ ("eval/preference_consistency_A", avg_score)
+ ) # Example metric name
+ print(f"Evaluation finished. Average 'A' preference score: {avg_score:.2f}")
+ else:
+ print("Evaluation completed with no samples processed.")
+
+ async def collect_trajectories(
+ self, item_tuple: Tuple
+ ) -> Tuple[Optional[ScoredDataGroup], List]:
+ # item_tuple is expected to contain one element: the conversation history (list of dicts)
+ raw_chat_history = item_tuple[0]
+ chat_for_generation = []
+ added_system_prompt_for_rl = False
+
+ # Optional: Inject RL preference string into system prompt with some probability
+ if random.random() < 0.05: # Small chance to directly prime the generator
+ chat_for_generation.append(
+ {
+ "role": "system",
+ "content": "Please respond in a way that aligns with this preference: "
+ + philosophical_preference_string,
+ }
+ )
+ added_system_prompt_for_rl = True
+
+ for msg_fset in raw_chat_history: # msg_fset is a frozenset of items
+ chat_for_generation.append(dict(msg_fset))
+
+ # Ensure last message isn't assistant, or remove it to get a prompt
+ if chat_for_generation and chat_for_generation[-1]["role"] == "assistant":
+ chat_for_generation.pop()
+ if (
+ not chat_for_generation or chat_for_generation[-1]["role"] == "assistant"
+ ): # Still ends with assistant or empty
+ print(
+ "Skipping trajectory collection: prompt ends with assistant or is empty after processing."
+ )
+ return None, []
+
+ # Check token length before generation
+ # Note: This tokenizer length check is approximate for the prompt only.
+ prompt_tokens = self.tokenizer.apply_chat_template(
+ chat_for_generation, tokenize=True, add_generation_prompt=False
+ )
+ # Max length for prompt should ensure (prompt + generated_response) fits
+ # self.config.max_token_length for tokenize_for_trainer.
+ if len(prompt_tokens) > (
+ self.config.max_token_length - self.config.generator_max_tokens
+ ):
+ print(
+ f"Skipping trajectory collection: prompt too long ({len(prompt_tokens)} tokens) "
+ f"for max_token_length budget ({self.config.max_token_length} - "
+ f"{self.config.generator_max_tokens})."
+ )
+ return None, []
+
+ # The previous check for effective_generator_context_window was for the API call itself,
+ # this new one is for downstream compatibility with tokenize_for_trainer.
+ # We should also respect the generator's own context window limit.
+ effective_generator_context_window = (
+ 3500 # Assuming gpt-3.5-turbo, give some buffer from 4096
+ )
+ if len(prompt_tokens) > (
+ effective_generator_context_window - self.config.generator_max_tokens
+ ):
+ print(
+ f"Skipping trajectory collection: prompt too long ({len(prompt_tokens)} tokens) "
+ f"for generator's own context window budget ({effective_generator_context_window} - "
+ f"{self.config.generator_max_tokens})."
+ )
+ return None, []
+
+ # Generate two responses (A and B)
+ # If we added a system prompt for RL, one response with it, one without, for variety
+ if added_system_prompt_for_rl:
+ resp1_future = self.server.chat_completion(
+ messages=chat_for_generation, # With RL system prompt
+ n=1,
+ max_tokens=self.config.generator_max_tokens,
+ temperature=0.7, # Allow some creativity
+ model=self.config.generator_model_name,
+ )
+ # Create a version of the chat without the injected RL system prompt for the second response
+ chat_for_generation_no_rl_prompt = [
+ m
+ for m in chat_for_generation
+ if not (
+ m["role"] == "system"
+ and philosophical_preference_string in m["content"]
+ )
+ ]
+ if (
+ not chat_for_generation_no_rl_prompt and chat_for_generation
+ ): # if all was system prompt
+ chat_for_generation_no_rl_prompt = (
+ chat_for_generation[1:]
+ if len(chat_for_generation) > 1
+ else chat_for_generation
+ )
+
+ resp2_future = self.server.chat_completion(
+ messages=chat_for_generation_no_rl_prompt, # Without RL system prompt
+ n=1,
+ max_tokens=self.config.generator_max_tokens,
+ temperature=0.7,
+ model=self.config.generator_model_name,
+ )
+ resp1, resp2 = await asyncio.gather(resp1_future, resp2_future)
+ # Combine choices:
+ # Need to ensure the structure matches what chat_completion would return for n=2
+ # This is a bit manual; ideally, the server handles n=2 better with mixed prompts
+ if resp1.choices and resp2.choices:
+ # Create a dummy completions object to hold both
+ class DummyChoice:
+ def __init__(self, message, finish_reason):
+ self.message = message
+ self.finish_reason = finish_reason
+
+ class DummyMessage:
+ def __init__(self, content):
+ self.content = content
+
+ # Ensure choices are valid before proceeding
+ if not resp1.choices[0].message or not resp2.choices[0].message:
+ print("Skipping due to invalid choices from generator.")
+ return None, []
+
+ chat_completions_choices = [
+ DummyChoice(
+ DummyMessage(resp1.choices[0].message.content),
+ resp1.choices[0].finish_reason,
+ ),
+ DummyChoice(
+ DummyMessage(resp2.choices[0].message.content),
+ resp2.choices[0].finish_reason,
+ ),
+ ]
+ else: # Not enough responses
+ print(
+ "Skipping trajectory collection: not enough responses from generator."
+ )
+ return None, []
+
+ else: # Standard generation of two diverse responses
+ completions_obj = await self.server.chat_completion(
+ messages=chat_for_generation,
+ n=2, # Generate two responses
+ max_tokens=self.config.generator_max_tokens,
+ temperature=0.7,
+ model=self.config.generator_model_name,
+ )
+ if not completions_obj or len(completions_obj.choices) < 2:
+ print(
+ "Skipping trajectory collection: not enough responses from generator (n=2 path)."
+ )
+ return None, []
+ chat_completions_choices = completions_obj.choices
+
+ # Prepare data for the judge
+ # The original prompt is `chat_for_generation`
+ # (or `chat_for_generation_no_rl_prompt` if that was used for B)
+ # For simplicity, use the prompt that led to respA as the "base" conversation for judging.
+
+ # This needs to be a list of ( (full_chat_A, finish_reason_A), (full_chat_B, finish_reason_B) )
+ # to pass to self.score
+ rollout_pair_for_scoring = []
+
+ response_a_content = chat_completions_choices[0].message.content
+ response_a_finish = chat_completions_choices[0].finish_reason
+ chat_A = chat_for_generation + [
+ {"role": "assistant", "content": response_a_content}
+ ]
+ rollout_pair_for_scoring.append((chat_A, response_a_finish))
+
+ response_b_content = chat_completions_choices[1].message.content
+ response_b_finish = chat_completions_choices[1].finish_reason
+ chat_B = chat_for_generation + [
+ {"role": "assistant", "content": response_b_content}
+ ]
+ rollout_pair_for_scoring.append((chat_B, response_b_finish))
+
+ # Call score to get the scored data. `score` expects a list of two items.
+ scored_data_group = await self.score(rollout_pair_for_scoring) # Pass the pair
+
+ return scored_data_group, [] # No backlog items for now
+
+ async def score(
+ self, rollout_pair_data: List[Tuple[List[Dict[str, str]], str]]
+ ) -> Optional[ScoredDataGroup]:
+ # rollout_pair_data is [(chat_A, finish_A), (chat_B, finish_B)]
+ if len(rollout_pair_data) < 2:
+ print("Score function received less than 2 rollouts to compare.")
+ return None
+
+ chat_A_full, finish_A = rollout_pair_data[0]
+ chat_B_full, finish_B = rollout_pair_data[1]
+
+ # Handle cases where one or both responses were cut off by length
+ # If both are length-limited, it's hard to judge preference, could skip or penalize both.
+ # If one is length-limited, it's likely worse.
+ if finish_A == "length" and finish_B == "length":
+ # Penalize both if we want to discourage long, incomplete answers
+ # For now, let's try to judge them anyway, but this could be a spot for different logic.
+ print("Both responses A and B hit length limit.")
+ elif finish_A == "length" or finish_B == "length":
+ print(
+ f"One response hit length limit: A_len_limit={finish_A == 'length'}, "
+ f"B_len_limit={finish_B == 'length'}"
+ )
+ # We could assign a strong negative score to the length-limited one here,
+ # or let the judge decide. For now, let judge decide.
+
+ # Prepare for the judge LLM
+ # The conversation context is the prompt part of chat_A (or chat_B, should be same up to 'assistant')
+ conversation_context_list = chat_A_full[
+ :-1
+ ] # All but the last assistant message
+ conversation_str = "\n".join(
+ [f"{msg['role']}: {msg['content']}" for msg in conversation_context_list]
+ )
+
+ response_a_content = chat_A_full[-1]["content"]
+ response_b_content = chat_B_full[-1]["content"]
+
+ # Create prompts for forward (A vs B) and reverse (B vs A) judging for robustness
+ fwd_judge_prompt_content = RLAIF_user_prompt_format_str.format(
+ conversation=conversation_str,
+ response_a=response_a_content,
+ response_b=response_b_content,
+ rl_preference_string=philosophical_preference_string,
+ )
+ # For reverse, swap A and B in the prompt
+ rvs_judge_prompt_content = RLAIF_user_prompt_format_str.format(
+ conversation=conversation_str,
+ response_a=response_b_content, # Swapped
+ response_b=response_a_content, # Swapped
+ rl_preference_string=philosophical_preference_string,
+ )
+
+ num_judgements_per_pair = 1 # Can increase for more robust scoring, e.g., 3
+
+ fwd_judge_future = self.server.chat_completion(
+ messages=[
+ {"role": "system", "content": judge_system_prompt},
+ {"role": "user", "content": fwd_judge_prompt_content},
+ ],
+ n=num_judgements_per_pair,
+ max_tokens=self.config.judge_max_tokens,
+ temperature=0.0, # Judge should be as deterministic as possible
+ model=self.config.judge_model_name,
+ )
+ rvs_judge_future = self.server.chat_completion(
+ messages=[
+ {"role": "system", "content": judge_system_prompt},
+ {"role": "user", "content": rvs_judge_prompt_content},
+ ],
+ n=num_judgements_per_pair,
+ max_tokens=self.config.judge_max_tokens,
+ temperature=0.0,
+ model=self.config.judge_model_name,
+ )
+
+ fwd_judge_responses, rvs_judge_responses = await asyncio.gather(
+ fwd_judge_future, rvs_judge_future
+ )
+
+ # Store one example judgement for wandb logging
+ if fwd_judge_responses.choices:
+ self.judgement_strings_buffer.append(
+ (
+ response_a_content,
+ response_b_content,
+ fwd_judge_responses.choices[0].message.content,
+ )
+ )
+
+ # Calculate scores from forward and reverse judgements
+ score_for_A = 0.0
+ score_for_B = 0.0
+
+ # Process forward judgements (Judge chose between A and B)
+ for choice in fwd_judge_responses.choices:
+ judgement_text = choice.message.content
+ # Extract \boxed{A} or \boxed{B}
+ chosen_val_match = re.search(r"\boxed{(A|B)}", judgement_text)
+ if chosen_val_match:
+ chosen = chosen_val_match.group(1)
+ if chosen == "A":
+ score_for_A += 1.0
+ elif chosen == "B":
+ score_for_B += 1.0
+
+ # Process reverse judgements (Judge chose between B (as A') and A (as B'))
+ for choice in rvs_judge_responses.choices:
+ judgement_text = choice.message.content
+ chosen_val_match = re.search(
+ r"\boxed{(A|B)}", judgement_text
+ ) # A here means original B, B means original A
+ if chosen_val_match:
+ chosen = chosen_val_match.group(1)
+ if chosen == "A": # Judge chose B (presented as A')
+ score_for_B += 1.0
+ elif chosen == "B": # Judge chose A (presented as B')
+ score_for_A += 1.0
+
+ total_judgements = (
+ 2 * num_judgements_per_pair
+ ) # Each pair judged forward and reverse
+
+ # Normalize scores: can be simple (preferred_score - non_preferred_score) or Bradley-Terry, etc.
+ # Here, let's use a simple difference normalized by total judgements, then mean-center.
+ # Effective score for A is (times A preferred) / total_judgements
+ # Effective score for B is (times B preferred) / total_judgements
+ # We want to assign these as rewards.
+ # For DPO, we often need one score for (chosen - rejected).
+ # Here, we have two rollouts (A and B). We give A `score_for_A` and B `score_for_B`.
+ # Let's normalize them so they sum to 0 for the pair to represent preference.
+
+ # If total_judgements is 0 (e.g. API error), or no clear preference.
+ if total_judgements == 0 or score_for_A + score_for_B == 0:
+ # No basis for preference, or judge failed. Could assign 0 or skip.
+ print(
+ "Judge failed to provide preference or API error. Assigning neutral scores."
+ )
+ final_score_A = 0.0
+ final_score_B = 0.0
+ else:
+ # Normalize scores to represent preference strength, e.g., ranging roughly -1 to 1
+ # A simple way: (score_A - score_B) / total_judgements can be one reward, and its negative for the other.
+ # Or, score A as (score_A / total_judgements) and B as (score_B / total_judgements)
+ # then normalize these (e.g., subtract mean).
+ # For PPO-style RL, each gets its own reward.
+ # Let's try: A_reward = score_for_A - score_for_B; B_reward = score_for_B - score_for_A
+ # Scaled by total_judgements
+
+ # We want a score for A and a score for B.
+ # Let's make them centered around 0 for the pair.
+ # Paired scores: (score_A - score_B) / total_judgements and (score_B - score_A) / total_judgements
+ if score_for_A > score_for_B:
+ final_score_A = 1.0
+ final_score_B = -1.0
+ elif score_for_B > score_for_A:
+ final_score_A = -1.0
+ final_score_B = 1.0
+ else: # Tie or no preference
+ final_score_A = 0.0
+ final_score_B = 0.0
+
+ # Handle length penalties explicitly if desired (could override judge scores)
+ if (
+ finish_A == "length" and final_score_A > -0.9
+ ): # If it was good but cut off, penalize
+ final_score_A = -1.0
+ if finish_B == "length" and final_score_B > -0.9:
+ final_score_B = -1.0
+
+ self.preference_scores_buffer.append(final_score_A)
+ self.preference_scores_buffer.append(final_score_B)
+
+ # Prepare ScoredDataGroup
+ scores_container = ScoredDataGroup()
+ scores_container["tokens"] = list()
+ scores_container["masks"] = list()
+ scores_container["scores"] = list()
+
+ for i, (full_chat, finish_reason) in enumerate(
+ [rollout_pair_data[0], rollout_pair_data[1]]
+ ):
+ tokenized_output = tokenize_for_trainer(
+ tokenizer=self.tokenizer,
+ chat=full_chat, # full conversation including assistant's response
+ finish_reason=finish_reason,
+ include_messages=self.config.include_messages,
+ )
+ if (
+ len(
+ [
+ mask_val
+ for mask_val in tokenized_output["masks"]
+ if mask_val != -100
+ ]
+ )
+ < 1
+ ):
+ continue # Skip if no assistant tokens to learn from
+
+ scores_container["tokens"].append(tokenized_output["tokens"])
+ scores_container["masks"].append(tokenized_output["masks"])
+ scores_container["scores"].append(
+ final_score_A if i == 0 else final_score_B
+ )
+
+ if not scores_container["tokens"]:
+ print("No valid tokens found for ScoredDataGroup after processing pair.")
+ return None
+
+ # Ensure scores are not the same if configured (for DPO-style data)
+ if (
+ self.config.ensure_scores_are_not_same
+ and len(scores_container["scores"]) >= 2
+ and scores_container["scores"][0] == scores_container["scores"][1]
+ ):
+ print(
+ f"Scores are the same ({scores_container['scores'][0]}) but "
+ "ensure_scores_are_not_same is True. Skipping pair."
+ )
+ # This can happen if judge gives no preference or if logic results in tie.
+ # For RLAIF leading to PPO, it's okay. For DPO, distinct preferred/rejected is needed.
+ # The current scoring final_score_A/B aims for -1/1, so this check is important.
+ # If they are same (e.g. both 0.0), it means no preference.
+ if (
+ scores_container["scores"][0] == 0.0
+ ): # If tie, this is a valid case of no preference.
+ pass # Allow ties if they are both zero (no preference)
+ else: # if scores are identical and non-zero, implies an issue or specific setup
+ return None
+
+ return scores_container
+
+ async def get_next_item(
+ self,
+ ) -> Tuple[List[frozenset], Dict, Dict]: # Matches BaseEnv signature more closely
+ if not self.train_dataset or len(self.train_dataset) == 0:
+ raise StopAsyncIteration("Dataset is empty or not loaded.")
+
+ next_raw_item = self.train_dataset[self.iter % len(self.train_dataset)]
+ self.iter += 1
+
+ # 'conversation' in WildChat is a list of dicts: [{'role': ..., 'content': ...}, ...]
+ conversation_history = next_raw_item.get("conversation", [])
+ if not conversation_history: # Should not happen with WildChat but good check
+ # Return an empty prompt or handle as error
+ return (
+ [],
+ {},
+ {
+ "id": next_raw_item.get("id", self.iter - 1),
+ "error": "empty_conversation",
+ },
+ )
+
+ # Convert to the frozenset format if BaseEnv expects it (original rlaif_server used this)
+ # My BaseEnv.get_next_item returns messages, metadata, correct_answer_optional
+ # Let's simplify what this get_next_item returns for now for collect_trajectories
+ # collect_trajectories was defined as item_tuple: Tuple
+ # The original rlaif_server.py in get_next_item returned (prompt_tuple,)
+ # where prompt_tuple was tuple of frozensets.
+ # Let's return List[Dict[str,str]] directly for the conversation
+
+ prompt_messages = [
+ dict(msg) for msg in conversation_history
+ ] # Ensure mutable dicts
+
+ # Return just the messages, collect_trajectories will handle it
+ # The tuple structure for item in collect_trajectories needs to be consistent.
+ # If BaseEnv.process expects get_next_item to return (messages, metadata, correct_answer)
+ # then we need to adhere. For now, let's assume collect_trajectories takes (messages_list,)
+ # This is a deviation from the stricter BaseEnv typing; might need adjustment if `process` complains.
+ # The original `rlaif_server.py` had get_next_item returning (prompt_frozenset_tuple, )
+ # and collect_trajectories took item[0] which was that tuple.
+ # Let's match that for now.
+ prompt_frozenset_tuple = tuple(
+ frozenset(msg.items()) for msg in prompt_messages
+ )
+ return (
+ prompt_frozenset_tuple,
+ {},
+ {},
+ ) # (messages_frozenset_tuple, metadata_dict, correct_answer_dict)
+
+
+if __name__ == "__main__":
+ PhilosophicalRLAIFEnv.cli()
diff --git a/environments/community/playwright_agent_env.py b/environments/community/playwright_agent_env.py
new file mode 100644
index 00000000..14eda5c3
--- /dev/null
+++ b/environments/community/playwright_agent_env.py
@@ -0,0 +1,488 @@
+from __future__ import annotations
+
+import asyncio
+import base64
+import json
+import os
+import tempfile
+import traceback
+from pathlib import Path
+from typing import List, Optional, Tuple, TypedDict
+
+# Playwright is used for browser automation
+from playwright.async_api import Browser, Page, async_playwright
+
+from atroposlib.envs.base import (
+ APIServerConfig,
+ BaseEnv,
+ BaseEnvConfig,
+ ScoredDataGroup,
+)
+from atroposlib.type_definitions import GameHistory, Item
+from atroposlib.utils.tokenize_for_trainer import tokenize_for_trainer
+
+# Gemini (google-genai) – optional, only imported when used to score a rollout
+try:
+ from google import genai # type: ignore
+ from google.genai import types as genai_types # type: ignore
+except ImportError: # pragma: no cover – gemini is optional in most dev environments
+ genai = None # type: ignore
+ genai_types = None # type: ignore
+
+
+class WebTask(TypedDict):
+ """Simple structure describing a task the agent should solve on the web."""
+
+ url: str
+ goal_description: str # natural language description of the objective
+ success_criterion: str # phrase we expect to appear somewhere (used as simple fallback if Gemini unavailable)
+
+
+class PlaywrightAgentEnv(BaseEnv):
+ """An environment that lets an LLM control a Playwright browser to accomplish a goal.
+
+ Each episode:
+ 1. Opens the target URL in a fresh Playwright context that records video.
+ 2. Repeatedly shows the current screenshot to the LLM and asks for the next JSON-encoded action.
+ 3. Executes the action (e.g. click, type, navigate).
+ 4. Stops when the LLM replies with {"action": "finish"} or the step-limit is reached.
+ 5. Uses Google Gemini to evaluate whether the goal was achieved from the recorded video
+ and computes a reward favouring both correctness and fewer steps.
+ """
+
+ name = "playwright_agent"
+ name_config_cls = BaseEnvConfig
+
+ # ---- configurable hyper-parameters ----
+ max_steps: int = 10 # maximum browser actions per episode
+ gemini_model_name: str = "gemini-2.5-pro-preview-05-06"
+
+ # ---------- lifecycle hooks ----------
+ async def setup(self) -> None: # type: ignore[override]
+ """Initialise Playwright and the list of tasks."""
+ # 1. Launch Playwright once and keep it for the lifetime of the env
+ self._playwright = await async_playwright().start()
+ self._browser: Browser = await self._playwright.chromium.launch(headless=True)
+
+ # 2. Load tasks from webvoyager_data.jsonl
+ self._tasks: List[WebTask] = []
+ try:
+ with open("data/webvoyager_data.jsonl", "r") as f:
+ for line in f:
+ if line.strip():
+ task_data = json.loads(line)
+ self._tasks.append(
+ {
+ "url": task_data["web"],
+ "goal_description": task_data["ques"],
+ # Using empty string for success_criterion as we're using Gemini to judge
+ "success_criterion": "",
+ }
+ )
+ print(f"Loaded {len(self._tasks)} tasks from webvoyager_data.jsonl")
+ except Exception as e:
+ print(f"Error loading tasks from webvoyager_data.jsonl: {e}")
+ # Fallback to a single example task if loading fails
+ self._tasks = [
+ {
+ "url": "https://example.com",
+ "goal_description": (
+ "Locate and open the link that contains the text 'More information'. "
+ "Then finish."
+ ),
+ "success_criterion": "More information",
+ }
+ ]
+
+ self._iter = 0
+
+ # Track if we're in development/test mode
+ self._dev_mode = os.environ.get("PLAYWRIGHT_ENV_DEV_MODE", "0") == "1"
+ if self._dev_mode:
+ print("Running in development mode - no LLM will be used")
+
+ async def teardown(self) -> None: # type: ignore[override]
+ if hasattr(self, "_browser"):
+ await self._browser.close()
+ if hasattr(self, "_playwright"):
+ await self._playwright.stop()
+
+ async def get_next_item(self) -> Item: # type: ignore[override]
+ """Return the next task specification."""
+ task = self._tasks[self._iter % len(self._tasks)]
+ self._iter += 1
+
+ # The prompt given to the LLM before any browser interaction
+ initial_prompt = (
+ frozenset(
+ {
+ "role": "user",
+ "content": (
+ f"You are an autonomous web-agent. Your goal is: "
+ f"{task['goal_description']}\n"
+ "You will be sent browser screenshots.\n"
+ "Reply with a JSON object describing the next action.\n\n"
+ "Allowed actions:\n"
+ " navigate – navigate the browser to \n"
+ " click – click the first element matching \n"
+ " type – type into element and press Enter\n"
+ " finish – if the goal is accomplished.\n\n"
+ 'Example: {"action": "click", "selector": "text=More information"}'
+ ),
+ }.items()
+ ),
+ )
+ # Ground truth is unknown at this stage – Gemini will judge later; we keep success_criterion for fallback
+ return (initial_prompt, task["success_criterion"], None)
+
+ # ---------------------------------------------------------------------
+ # core rollout – interacts with browser, builds messages & returns scores
+ # ---------------------------------------------------------------------
+ async def collect_trajectories(self, item: Item) -> Tuple[GameHistory | None, List[Item]]: # type: ignore[override]
+ prompt_frozenset, success_criterion, _ = (
+ item # we stored criterion in position 1
+ )
+
+ # Extract content string properly
+ prompt_dict = dict(prompt_frozenset)
+ content = prompt_dict.get("content", "")
+
+ # Handle different content structures to extract goal description
+ goal_description = ""
+ if isinstance(content, str):
+ goal_description = content
+ elif isinstance(content, list):
+ # If content is a list of message parts, extract text parts
+ for part in content:
+ if isinstance(part, dict) and part.get("type") == "text":
+ goal_description += part.get("text", "")
+ elif isinstance(content, dict):
+ # Try to extract text from a dict structure
+ if "text" in content:
+ goal_description = content["text"]
+
+ # Ensure we have a string
+ goal_description = str(goal_description)
+
+ # If we couldn't extract a meaningful description, use a fallback
+ if not goal_description:
+ goal_description = "No goal description available"
+
+ # 1. Create new context+page with video recording
+ tmp_dir = Path(tempfile.mkdtemp(prefix="playwright_run_"))
+ context = await self._browser.new_context(record_video_dir=str(tmp_dir))
+ page: Page = await context.new_page()
+
+ # Extract target URL from goal description heuristically (fallback)
+ target_url = self._extract_first_url(goal_description) or "about:blank"
+ if target_url != "about:blank":
+ try:
+ await page.goto(target_url, wait_until="load")
+ except Exception:
+ traceback.print_exc()
+
+ messages_for_llm: List[dict] = [
+ dict(prompt_frozenset)
+ ] # start conversation history
+
+ steps_taken = 0
+ finished = False
+ screenshot_b64 = ""
+
+ # In development mode, we'll just take one screenshot and finish
+ if self._dev_mode:
+ try:
+ screenshot_bytes = await page.screenshot(full_page=True)
+ screenshot_b64 = base64.b64encode(screenshot_bytes).decode("utf-8")
+ print(f"Development mode: took screenshot of {target_url}")
+ finished = True
+ except Exception as e:
+ print(f"Development mode: error taking screenshot: {e}")
+ screenshot_b64 = ""
+ steps_taken = 1
+ else:
+ # Normal mode with LLM interaction
+ while steps_taken < self.max_steps and not finished:
+ # ---- 1. capture screenshot ----
+ try:
+ screenshot_bytes = await page.screenshot(full_page=True)
+ screenshot_b64 = base64.b64encode(screenshot_bytes).decode("utf-8")
+ except Exception:
+ screenshot_b64 = ""
+
+ # ---- 2. Ask LLM for next action ----
+ user_msg = {
+ "role": "user",
+ "content": [
+ {
+ "type": "text",
+ "text": f"Step {steps_taken}. Provide the next action as JSON.",
+ },
+ {
+ "type": "image_url",
+ "image_url": {
+ "url": f"data:image/png;base64,{screenshot_b64}",
+ },
+ },
+ ],
+ }
+ messages_for_llm.append(user_msg)
+
+ llm_response = await self.server.chat_completion(
+ messages=messages_for_llm,
+ n=1,
+ max_tokens=256,
+ timeout=60,
+ )
+
+ assistant_content = llm_response.choices[0].message.content.strip()
+ messages_for_llm.append(
+ {"role": "assistant", "content": assistant_content}
+ )
+
+ # ---- 3. Execute LLM-proposed action ----
+ try:
+ action_dict = json.loads(assistant_content)
+ action_name = action_dict.get("action")
+ except Exception:
+ # malformed JSON → give up this episode
+ break
+
+ try:
+ if action_name == "finish":
+ finished = True
+ elif action_name == "navigate":
+ await page.goto(action_dict["url"], wait_until="load")
+ elif action_name == "click":
+ await page.locator(action_dict["selector"]).first.click()
+ elif action_name == "type":
+ await page.locator(action_dict["selector"]).first.fill(
+ action_dict["text"]
+ )
+ await page.keyboard.press("Enter")
+ else:
+ # unsupported → no-op
+ pass
+ except Exception:
+ traceback.print_exc()
+
+ steps_taken += 1
+
+ # simple heuristic exit if success text present and Gemini not available
+ if (
+ not finished
+ and success_criterion
+ and success_criterion.lower() in (await page.content()).lower()
+ ):
+ finished = True
+
+ # Finalise the Playwright context and obtain video path
+ try:
+ await context.close()
+ except Exception:
+ traceback.print_exc()
+ video_path: Optional[str] = None
+ try:
+ video_rel = next(tmp_dir.glob("**/*.webm")) # Playwright stores .webm
+ video_path = str(video_rel)
+ except StopIteration:
+ pass
+
+ # ---------------------------------------------------
+ # Evaluate episode outcome – Gemini if available
+ # ---------------------------------------------------
+ success = (
+ True
+ if self._dev_mode
+ else await self._judge_success_with_gemini(
+ video_path, goal_description, success_criterion
+ )
+ )
+
+ reward_value = self._compute_reward(success, steps_taken)
+
+ # ----------------------------------
+ # format for trainer (single sample)
+ # ----------------------------------
+ if self._dev_mode:
+ # In dev mode, create minimal return structure without tokenizer
+ scored_group = ScoredDataGroup(
+ tokens=[[0]], # minimal token placeholder
+ masks=[[1]], # minimal mask placeholder
+ scores=[reward_value],
+ images=[screenshot_b64],
+ )
+ else:
+ out_dict = tokenize_for_trainer(self.tokenizer, tuple(messages_for_llm))
+ tokens = out_dict["tokens"]
+ masks = out_dict["masks"]
+
+ scored_group = ScoredDataGroup(
+ tokens=[tokens],
+ masks=[masks],
+ scores=[reward_value],
+ images=[screenshot_b64],
+ )
+ return scored_group, []
+
+ # ------------------------------------------------------------------
+ # helper methods
+ # ------------------------------------------------------------------
+ async def _judge_success_with_gemini(
+ self,
+ video_path: Optional[str],
+ goal_description: str,
+ success_criterion: str | None,
+ ) -> bool:
+ """Return True if the episode is deemed successful."""
+ # Fallback cheap check – use DOM text in last page
+ if genai is None or video_path is None or not Path(video_path).exists():
+ # Very simple heuristic using success_criterion (already checked earlier)
+ return success_criterion is None or success_criterion == ""
+
+ try:
+ client = genai.Client(api_key=os.environ.get("GEMINI_API_KEY"))
+ file_res = client.files.upload(file=video_path)
+ contents = [
+ genai_types.Content(
+ role="user",
+ parts=[
+ genai_types.Part.from_uri(
+ file_uri=file_res.uri,
+ mime_type=file_res.mime_type,
+ ),
+ genai_types.Part.from_text(
+ text=(
+ "Did the agent in this video succeed in the following task?\n"
+ f"Task: {goal_description}\n"
+ "Answer with a single word – Yes or No."
+ )
+ ),
+ ],
+ )
+ ]
+ gen_config = genai_types.GenerateContentConfig(
+ response_mime_type="text/plain"
+ )
+ # Gemini streaming → collect text
+ text_chunks: List[str] = []
+ for chunk in client.models.generate_content_stream(
+ model=self.gemini_model_name,
+ contents=contents,
+ config=gen_config,
+ ):
+ if hasattr(chunk, "text"):
+ text_chunks.append(chunk.text)
+ judgment = " ".join(text_chunks).strip().lower()
+ return "yes" in judgment
+ except Exception:
+ traceback.print_exc()
+ return False
+
+ def _compute_reward(self, success: bool, steps_taken: int) -> float:
+ if not success:
+ return -1.0
+ # successful – normalise by steps (fewer is better)
+ return 1.0 + max(0, (self.max_steps - steps_taken)) / self.max_steps
+
+ @staticmethod
+ def _extract_first_url(text: str) -> Optional[str]:
+ import re
+
+ # Handle None or non-string type
+ if text is None or not isinstance(text, str):
+ return None
+
+ match = re.search(r"https?://[\w/.:\-]+", text)
+ return match.group(0) if match else None
+
+ async def evaluate(self, *args, **kwargs) -> None:
+ """
+ Evaluation method required by BaseEnv.
+ Called periodically during training to assess model performance.
+
+ This environment doesn't use custom evaluation metrics.
+ """
+ return None
+
+ # -----------------------------------------------------
+ # mandatory classmethod for config initialisation
+ # -----------------------------------------------------
+ @classmethod
+ def config_init(cls): # type: ignore[override]
+ base_config = BaseEnvConfig(
+ wandb_name="playwright_agent",
+ tokenizer_name="Qwen/Qwen-tokenizer",
+ group_size=1, # we only need one rollout per episode for this env
+ use_wandb=True,
+ max_num_workers=1,
+ rollout_server_url="http://localhost:8000",
+ total_steps=500,
+ batch_size=4,
+ steps_per_eval=50,
+ max_token_length=2048,
+ )
+
+ server_configs = [
+ APIServerConfig(
+ model_name="Qwen/Qwen-tokenizer",
+ base_url="http://localhost:9001/v1",
+ api_key="x",
+ num_requests_for_eval=64,
+ )
+ ]
+
+ return base_config, server_configs
+
+
+if __name__ == "__main__":
+ import sys
+
+ # Add simple development mode option
+ if len(sys.argv) > 1 and sys.argv[1] == "dev":
+ print("Starting PlaywrightAgentEnv in development mode...")
+ os.environ["PLAYWRIGHT_ENV_DEV_MODE"] = "1"
+
+ async def test_env():
+ # Get the default configurations
+ default_config, default_server_configs = PlaywrightAgentEnv.config_init()
+
+ # Create the environment with the default configs
+ env = PlaywrightAgentEnv(
+ config=default_config,
+ server_configs=default_server_configs,
+ testing=True,
+ )
+ await env.setup()
+
+ # Create a simple test item
+ test_task = {
+ "url": "https://example.com",
+ "goal_description": "Locate and click on the 'More information' link.",
+ "success_criterion": "More information",
+ }
+
+ # Create a simple prompt
+ prompt_set = frozenset(
+ {
+ "role": "user",
+ "content": f"Test goal: {test_task['goal_description']}",
+ }.items()
+ )
+
+ item = (prompt_set, test_task["success_criterion"], None)
+
+ try:
+ result, _ = await env.collect_trajectories(item)
+ print("Successfully ran test trajectory")
+ print(f"Result: {result}")
+ except Exception as e:
+ print(f"Error in test trajectory: {e}")
+ traceback.print_exc()
+ finally:
+ await env.teardown()
+
+ asyncio.run(test_env())
+ else:
+ # Run CLI helper if invoked directly (inherits from BaseEnv)
+ PlaywrightAgentEnv.cli()
diff --git a/environments/community/router_env/.env.example b/environments/community/router_env/.env.example
new file mode 100644
index 00000000..f9b504fe
--- /dev/null
+++ b/environments/community/router_env/.env.example
@@ -0,0 +1 @@
+OPENAI_API_KEY="your_openai_api_key_here"
diff --git a/environments/community/router_env/.gitignore b/environments/community/router_env/.gitignore
new file mode 100644
index 00000000..5c0e0550
--- /dev/null
+++ b/environments/community/router_env/.gitignore
@@ -0,0 +1,155 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds peculation files. Make sure to keep the
+# template files.
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*_model
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to p
+# Pipfile.lock
+
+# poetry
+# Uncomment if you are using poetry.
+# poetry.lock
+
+# pdm
+# Uncomment if you are using pdm.
+# pdm.lock
+# .pdm.toml
+
+# PEP 582; remove simple
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static analyzer
+.pytype/
+
+# Cython cache files
+cython_debug/
+
+# Language Server Protocol
+.lsp_boosterik/
+.metals/
+.classpath
+.project
+.settings
+.tabs
+
+# Router RL Env specific (if any HTML/JSONL outputs are not meant for commit)
+# router_rl_env_rollouts.html
+# router_rl_env_rollouts.jsonl
+# router_rl_env_eval_rollouts.html
+# router_rl_env_eval_rollouts.jsonl
diff --git a/environments/community/router_env/engine/README.md b/environments/community/router_env/engine/README.md
new file mode 100644
index 00000000..d1b43372
--- /dev/null
+++ b/environments/community/router_env/engine/README.md
@@ -0,0 +1,3 @@
+# Engine
+
+This directory contains the backend services and AI agents for the Pebble (Stone UI) project.
diff --git a/environments/community/router_env/engine/agents/agent_mcp_mvp.py b/environments/community/router_env/engine/agents/agent_mcp_mvp.py
new file mode 100644
index 00000000..65d55d08
--- /dev/null
+++ b/environments/community/router_env/engine/agents/agent_mcp_mvp.py
@@ -0,0 +1,61 @@
+import logging
+import os
+from pathlib import Path
+
+from dotenv import load_dotenv
+from livekit.agents import Agent, AgentSession, JobContext, WorkerOptions, cli, mcp
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("mcp-agent")
+
+load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")
+
+
+class MyAgent(Agent):
+ def __init__(self) -> None:
+ super().__init__(
+ instructions=(
+ "You can retrieve data via the MCP server. The interface is voice-based: "
+ "accept spoken user queries and respond with synthesized speech."
+ ),
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ mcp_servers=[
+ mcp.MCPServerHTTP(
+ url=(
+ "https://mcp.gumloop.com/gcalendar/"
+ "cY3bcaFS1qNdeVBnj0XIhnP4FEp2%3Aae99858e75594251bea9e05f32bb99b3"
+ ),
+ timeout=5,
+ client_session_timeout_seconds=5,
+ ),
+ ],
+ )
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=MyAgent(), room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint, agent_name="mcp-agent"))
diff --git a/environments/community/router_env/engine/agents/ask_agent.py b/environments/community/router_env/engine/agents/ask_agent.py
new file mode 100644
index 00000000..bd5c727d
--- /dev/null
+++ b/environments/community/router_env/engine/agents/ask_agent.py
@@ -0,0 +1,197 @@
+import asyncio
+import logging
+import os
+
+from dotenv import load_dotenv
+from livekit.agents import mcp
+from livekit.agents.llm import LLM, ChatContext # Removed ChatRole as using strings
+from livekit.plugins import openai
+
+logger = logging.getLogger("text-perplexity-agent")
+logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+# --- Configure Perplexity MCP Server (as a function to allow async context management) ---
+def get_perplexity_mcp_server():
+ if os.environ.get("PERPLEXITY_API_KEY"):
+ mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "perplexity",
+ "perplexity-ask",
+ "dist",
+ "index.js",
+ )
+ )
+
+ if not os.path.exists(mcp_script_path):
+ logger.error(
+ f"❌ MCP script not found at {mcp_script_path}. Make sure you've run "
+ "'npm install && npm run build' in the server directory."
+ )
+ logger.warning("⚠️ Perplexity tools will be unavailable.")
+ return None
+ else:
+ logger.info(
+ f"📂 Configuring Perplexity MCP server with script: {mcp_script_path}"
+ )
+ return mcp.MCPServerStdio(
+ name="PerplexityStdioServer",
+ params={
+ "command": "node",
+ "args": [mcp_script_path],
+ "cwd": os.path.dirname(mcp_script_path),
+ "env": {
+ "PERPLEXITY_API_KEY": os.environ.get("PERPLEXITY_API_KEY") or ""
+ },
+ "client_session_timeout_seconds": 30,
+ },
+ client_session_timeout_seconds=30,
+ )
+ else:
+ logger.warning(
+ "⚠️ PERPLEXITY_API_KEY not set. Perplexity tools will be unavailable."
+ )
+ return None
+
+
+async def run_chat_loop(
+ llm_instance: LLM,
+ p_mcp_server: mcp.MCPServerStdio | None,
+ initial_question: str = None,
+):
+ """Runs a text-based chat loop with the LLM and Perplexity tool."""
+
+ chat_context = ChatContext()
+
+ system_prompt = """
+ You are a specialized assistant for answering general knowledge questions, providing explanations,
+ and performing web searches using the 'perplexity_ask' tool.
+ When the user asks for information, facts, or to 'search the web', you are the designated expert.
+ When calling the 'perplexity_ask' tool, ensure the 'messages' argument is an array containing
+ a single object with 'role': 'user' and 'content' set to the user's question.
+ For example: {"messages": [{"role": "user", "content": "What is the capital of France?"}]}
+ You do not have other tools. Do not try to delegate.
+ """
+ chat_context.add_message(role="system", content=system_prompt)
+
+ async def process_question(question: str):
+ logger.info(f"You: {question}")
+ chat_context.add_message(role="user", content=question)
+
+ full_response = ""
+ logger.info("Agent:")
+
+ mcp_servers_to_use = []
+ if p_mcp_server:
+ # MCPServerStdio is managed by async with in main, so it should be running
+ mcp_servers_to_use.append(p_mcp_server)
+ logger.info("Perplexity MCP Server is available for this query.")
+
+ try:
+ logger.info(f"DEBUG: Type of chat_context: {type(chat_context)}")
+ logger.info(f"DEBUG: Attributes of chat_context: {dir(chat_context)}")
+ # Pass messages from ChatContext and the list of mcp_servers
+ async for chunk in llm_instance.chat(
+ messages=chat_context.messages, mcp_servers=mcp_servers_to_use
+ ):
+ if chunk.delta.content:
+ print(chunk.delta.content, end="", flush=True)
+ full_response += chunk.delta.content
+ if chunk.delta.tool_calls:
+ logger.info(f"\n[Tool call detected: {chunk.delta.tool_calls}]")
+ except Exception as e:
+ logger.error(f"Error during LLM chat: {e}")
+ print(f"Sorry, I encountered an error: {e}")
+ return
+
+ print()
+ chat_context.add_message(role="assistant", content=full_response)
+
+ if initial_question:
+ await process_question(initial_question)
+
+ while True:
+ try:
+ user_input = await asyncio.to_thread(input, "You: ")
+ if user_input.lower() in ["exit", "quit"]:
+ logger.info("Exiting chat.")
+ break
+ if not user_input.strip():
+ continue
+ await process_question(user_input)
+ except KeyboardInterrupt:
+ logger.info("\nExiting chat due to interrupt.")
+ break
+ except EOFError:
+ logger.info("\nExiting chat due to EOF.")
+ break
+
+
+async def main():
+ """Main entrypoint for the text-based Perplexity agent."""
+ logger.info("Starting Text-based Perplexity Agent...")
+
+ llm_instance = openai.LLM(model="gpt-4o")
+
+ p_mcp_server_instance = get_perplexity_mcp_server()
+
+ test_question = "What is the capital of France?"
+
+ if p_mcp_server_instance:
+ try:
+ # await p_mcp_server_instance.connect() # Connect to MCP server -> Removed
+ logger.info(
+ "Perplexity MCP Server instance created. Will be used by LLM if needed."
+ )
+ await run_chat_loop(
+ llm_instance, p_mcp_server_instance, initial_question=test_question
+ )
+ finally:
+ logger.info(
+ "Closing Perplexity MCP server resources."
+ ) # Changed log message
+ await p_mcp_server_instance.aclose() # Close MCP server connection
+ else:
+ logger.warning("Running chat loop without Perplexity MCP server.")
+ await run_chat_loop(llm_instance, None, initial_question=test_question)
+
+ logger.info("Text-based Perplexity Agent finished.")
+
+
+if __name__ == "__main__":
+ if not os.environ.get("PERPLEXITY_API_KEY"):
+ logger.error("🔴 PERPLEXITY_API_KEY is not set in the environment.")
+ logger.error(
+ "🔴 Please set it in your .env file for the agent to function correctly with Perplexity."
+ )
+
+ if os.environ.get("PERPLEXITY_API_KEY"):
+ mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "perplexity",
+ "perplexity-ask",
+ "dist",
+ "index.js",
+ )
+ )
+ if not os.path.exists(mcp_script_path):
+ logger.error(f"❌ Critical: MCP script not found at {mcp_script_path}.")
+ logger.error(
+ "❌ The agent cannot use Perplexity tools. Please build the MCP server "
+ "('npm install && npm run build' in its directory)."
+ )
+ exit(1)
+
+ asyncio.run(main())
diff --git a/environments/community/router_env/engine/agents/ask_agent_temp.py b/environments/community/router_env/engine/agents/ask_agent_temp.py
new file mode 100644
index 00000000..bd5c727d
--- /dev/null
+++ b/environments/community/router_env/engine/agents/ask_agent_temp.py
@@ -0,0 +1,197 @@
+import asyncio
+import logging
+import os
+
+from dotenv import load_dotenv
+from livekit.agents import mcp
+from livekit.agents.llm import LLM, ChatContext # Removed ChatRole as using strings
+from livekit.plugins import openai
+
+logger = logging.getLogger("text-perplexity-agent")
+logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+# --- Configure Perplexity MCP Server (as a function to allow async context management) ---
+def get_perplexity_mcp_server():
+ if os.environ.get("PERPLEXITY_API_KEY"):
+ mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "perplexity",
+ "perplexity-ask",
+ "dist",
+ "index.js",
+ )
+ )
+
+ if not os.path.exists(mcp_script_path):
+ logger.error(
+ f"❌ MCP script not found at {mcp_script_path}. Make sure you've run "
+ "'npm install && npm run build' in the server directory."
+ )
+ logger.warning("⚠️ Perplexity tools will be unavailable.")
+ return None
+ else:
+ logger.info(
+ f"📂 Configuring Perplexity MCP server with script: {mcp_script_path}"
+ )
+ return mcp.MCPServerStdio(
+ name="PerplexityStdioServer",
+ params={
+ "command": "node",
+ "args": [mcp_script_path],
+ "cwd": os.path.dirname(mcp_script_path),
+ "env": {
+ "PERPLEXITY_API_KEY": os.environ.get("PERPLEXITY_API_KEY") or ""
+ },
+ "client_session_timeout_seconds": 30,
+ },
+ client_session_timeout_seconds=30,
+ )
+ else:
+ logger.warning(
+ "⚠️ PERPLEXITY_API_KEY not set. Perplexity tools will be unavailable."
+ )
+ return None
+
+
+async def run_chat_loop(
+ llm_instance: LLM,
+ p_mcp_server: mcp.MCPServerStdio | None,
+ initial_question: str = None,
+):
+ """Runs a text-based chat loop with the LLM and Perplexity tool."""
+
+ chat_context = ChatContext()
+
+ system_prompt = """
+ You are a specialized assistant for answering general knowledge questions, providing explanations,
+ and performing web searches using the 'perplexity_ask' tool.
+ When the user asks for information, facts, or to 'search the web', you are the designated expert.
+ When calling the 'perplexity_ask' tool, ensure the 'messages' argument is an array containing
+ a single object with 'role': 'user' and 'content' set to the user's question.
+ For example: {"messages": [{"role": "user", "content": "What is the capital of France?"}]}
+ You do not have other tools. Do not try to delegate.
+ """
+ chat_context.add_message(role="system", content=system_prompt)
+
+ async def process_question(question: str):
+ logger.info(f"You: {question}")
+ chat_context.add_message(role="user", content=question)
+
+ full_response = ""
+ logger.info("Agent:")
+
+ mcp_servers_to_use = []
+ if p_mcp_server:
+ # MCPServerStdio is managed by async with in main, so it should be running
+ mcp_servers_to_use.append(p_mcp_server)
+ logger.info("Perplexity MCP Server is available for this query.")
+
+ try:
+ logger.info(f"DEBUG: Type of chat_context: {type(chat_context)}")
+ logger.info(f"DEBUG: Attributes of chat_context: {dir(chat_context)}")
+ # Pass messages from ChatContext and the list of mcp_servers
+ async for chunk in llm_instance.chat(
+ messages=chat_context.messages, mcp_servers=mcp_servers_to_use
+ ):
+ if chunk.delta.content:
+ print(chunk.delta.content, end="", flush=True)
+ full_response += chunk.delta.content
+ if chunk.delta.tool_calls:
+ logger.info(f"\n[Tool call detected: {chunk.delta.tool_calls}]")
+ except Exception as e:
+ logger.error(f"Error during LLM chat: {e}")
+ print(f"Sorry, I encountered an error: {e}")
+ return
+
+ print()
+ chat_context.add_message(role="assistant", content=full_response)
+
+ if initial_question:
+ await process_question(initial_question)
+
+ while True:
+ try:
+ user_input = await asyncio.to_thread(input, "You: ")
+ if user_input.lower() in ["exit", "quit"]:
+ logger.info("Exiting chat.")
+ break
+ if not user_input.strip():
+ continue
+ await process_question(user_input)
+ except KeyboardInterrupt:
+ logger.info("\nExiting chat due to interrupt.")
+ break
+ except EOFError:
+ logger.info("\nExiting chat due to EOF.")
+ break
+
+
+async def main():
+ """Main entrypoint for the text-based Perplexity agent."""
+ logger.info("Starting Text-based Perplexity Agent...")
+
+ llm_instance = openai.LLM(model="gpt-4o")
+
+ p_mcp_server_instance = get_perplexity_mcp_server()
+
+ test_question = "What is the capital of France?"
+
+ if p_mcp_server_instance:
+ try:
+ # await p_mcp_server_instance.connect() # Connect to MCP server -> Removed
+ logger.info(
+ "Perplexity MCP Server instance created. Will be used by LLM if needed."
+ )
+ await run_chat_loop(
+ llm_instance, p_mcp_server_instance, initial_question=test_question
+ )
+ finally:
+ logger.info(
+ "Closing Perplexity MCP server resources."
+ ) # Changed log message
+ await p_mcp_server_instance.aclose() # Close MCP server connection
+ else:
+ logger.warning("Running chat loop without Perplexity MCP server.")
+ await run_chat_loop(llm_instance, None, initial_question=test_question)
+
+ logger.info("Text-based Perplexity Agent finished.")
+
+
+if __name__ == "__main__":
+ if not os.environ.get("PERPLEXITY_API_KEY"):
+ logger.error("🔴 PERPLEXITY_API_KEY is not set in the environment.")
+ logger.error(
+ "🔴 Please set it in your .env file for the agent to function correctly with Perplexity."
+ )
+
+ if os.environ.get("PERPLEXITY_API_KEY"):
+ mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "perplexity",
+ "perplexity-ask",
+ "dist",
+ "index.js",
+ )
+ )
+ if not os.path.exists(mcp_script_path):
+ logger.error(f"❌ Critical: MCP script not found at {mcp_script_path}.")
+ logger.error(
+ "❌ The agent cannot use Perplexity tools. Please build the MCP server "
+ "('npm install && npm run build' in its directory)."
+ )
+ exit(1)
+
+ asyncio.run(main())
diff --git a/environments/community/router_env/engine/agents/calc_agent.py b/environments/community/router_env/engine/agents/calc_agent.py
new file mode 100644
index 00000000..07809b65
--- /dev/null
+++ b/environments/community/router_env/engine/agents/calc_agent.py
@@ -0,0 +1,121 @@
+import logging # Added logging
+import os
+from typing import List, Optional # Add Optional & List import
+
+from livekit.agents import mcp # Corrected import for mcp
+from livekit.agents import tts # Corrected import for tts module
+from livekit.agents import ( # Changed import; Add ChatContext import
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+)
+from livekit.agents.llm import ( # Added function_tool for delegate_to_router_agent if it were defined here
+ ChatChunk,
+ function_tool,
+)
+from livekit.agents.types import NOT_GIVEN # Corrected import for NOT_GIVEN
+from livekit.agents.voice import Agent, AgentSession
+from livekit.plugins import deepgram, openai, silero
+
+# Removed: from mcp_client import MCPServerStdio
+# Removed: from mcp_client.agent_tools import MCPToolsIntegration
+from livekit.plugins.turn_detector.multilingual import ( # Added from official example
+ MultilingualModel,
+)
+
+logger = logging.getLogger("agent-math-official") # Added logger
+
+mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__), "..", "tools", "mcp", "calc", "calc_server.py"
+ )
+)
+
+
+class CalculatorAgent(Agent):
+ """A LiveKit agent that uses MCP tools from one or more MCP servers."""
+
+ def __init__(
+ self,
+ chat_ctx: ChatContext,
+ instructions: Optional[str] = None,
+ mcp_servers: Optional[list[mcp.MCPServer]] = None,
+ tts: Optional[tts.TTS] = NOT_GIVEN,
+ tools: Optional[List[function_tool]] = None,
+ ): # Added tools parameter
+
+ final_instructions = (
+ instructions
+ if instructions is not None
+ else """
+ You are a specialist Math assistant. Your expertise is in solving mathematical problems,
+ performing calculations, arithmetic, and answering questions about numbers.
+ You have two calculation tools: 'multiply' and 'add'.
+ When your current math task is complete, or if the user asks for something not related to math,
+ you MUST use the 'delegate_to_router_agent' tool to return to the main assistant.
+ """
+ )
+
+ # Combine passed tools with any class-defined tools if necessary (none here for now)
+ all_tools = tools if tools is not None else []
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ mcp_servers=[
+ mcp.MCPServerStdio(
+ command="python",
+ args=[mcp_script_path],
+ )
+ # MODIFIED: Removed chat_ctx=chat_ctx argument
+ ],
+ tools=all_tools, # Pass the tools to the parent Agent class
+ )
+ # MCP tools are automatically integrated by AgentSession if mcp_servers is configured.
+ # No need for MCPToolsIntegration or manually adding tools here.
+
+ async def llm_node(self, chat_ctx, tools, model_settings):
+ """Override the llm_node to say a message when a tool call is detected."""
+ tool_call_detected = False
+
+ async for chunk in super().llm_node(chat_ctx, tools, model_settings):
+ if (
+ isinstance(chunk, ChatChunk)
+ and chunk.delta
+ and chunk.delta.tool_calls
+ and not tool_call_detected
+ ):
+ tool_call_detected = True
+ # Example: if self.tts: self.session.say("Working on the math problem.")
+ # Currently, Math agent does not say anything here.
+ yield chunk
+
+ async def on_enter(self):
+ # when the agent is added to the session, we'll initiate the conversation by
+ # using the LLM to generate a reply
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ """Main entrypoint for the LiveKit agent application."""
+ await ctx.connect() # Connect earlier as in official example
+
+ # Directly configure AgentSession with mcp_servers
+ session = AgentSession(
+ vad=silero.VAD.load(), # Redundant if agent has it, but official example does this
+ stt=deepgram.STT(model="nova-2", language="en-US"), # Consistent with agent
+ llm=openai.LLM(model="gpt-4o"), # Consistent with agent
+ tts=openai.TTS(voice="alloy"), # Consistent with agent
+ turn_detection=MultilingualModel(), # Consistent with agent
+ )
+
+ # Instantiate the agent
+ agent = CalculatorAgent(chat_ctx=session._chat_ctx)
+
+ await session.start(agent=agent, room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/calendar_agent.py b/environments/community/router_env/engine/agents/calendar_agent.py
new file mode 100644
index 00000000..7bda661e
--- /dev/null
+++ b/environments/community/router_env/engine/agents/calendar_agent.py
@@ -0,0 +1,79 @@
+import logging
+import os
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+ mcp,
+)
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("calendar-agent")
+
+
+class CalendarAgent(Agent):
+ def __init__(
+ self, chat_ctx: ChatContext, tools: Optional[List[function_tool]] = None
+ ) -> None:
+
+ final_instructions = (
+ "You are a Calendar specialist. You can help with scheduling, creating, modifying, or "
+ "querying calendar events, appointments, and meetings. "
+ "Use tools like 'create_calendar_event', 'get_calendar_events', etc., when available. "
+ "If your task is complete or the user asks for something outside your calendar capabilities "
+ "(e.g., math, web search), you MUST use the 'delegate_to_router_agent' tool to return to "
+ "the main assistant."
+ )
+
+ all_tools = tools if tools is not None else []
+
+ mcp_servers_list = []
+ gumloop_mcp_url = os.getenv("GUMLOOP_CALENDAR_MCP_URL")
+ if gumloop_mcp_url:
+ mcp_servers_list.append(
+ mcp.MCPServerHTTP(
+ url=gumloop_mcp_url,
+ timeout=5,
+ client_session_timeout_seconds=5,
+ )
+ )
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ tools=all_tools,
+ mcp_servers=mcp_servers_list,
+ )
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=CalendarAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/caller_agent.py b/environments/community/router_env/engine/agents/caller_agent.py
new file mode 100644
index 00000000..b94661bb
--- /dev/null
+++ b/environments/community/router_env/engine/agents/caller_agent.py
@@ -0,0 +1,86 @@
+import logging
+import os
+import random
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit import api
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ RunContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+)
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+logger = logging.getLogger("caller-agent")
+
+
+class CallerAgent(Agent):
+ def __init__(
+ self, chat_ctx: ChatContext, tools: Optional[List[function_tool]] = None
+ ) -> None:
+
+ final_instructions = (
+ "You are a Caller specialist. Your primary function is to initiate phone calls. "
+ + "If the user asks to call someone, use the 'make_phone_call' tool. "
+ + "Currently, you can only call a predefined contact (Sam at +16467085301). "
+ + "Confirm with the user if they want to call this specific contact. "
+ + "If your task is complete or the user asks for something outside your calling "
+ + "capabilities (e.g., math, web search), you MUST use the 'delegate_to_router_agent' "
+ + "tool to return to the main assistant."
+ )
+
+ agent_tools = [self.make_phone_call]
+ all_tools = agent_tools + (tools if tools is not None else [])
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ tools=all_tools,
+ )
+ self.lkapi = api.LiveKitAPI()
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+ @function_tool
+ async def make_phone_call(self, context: RunContext, phone_number: str):
+ """
+ Call this function to make a phone call to a user number.
+ Args:
+ phone_number: The phone number to call.
+ """
+ await self.lkapi.agent_dispatch.create_dispatch(
+ api.CreateAgentDispatchRequest(
+ agent_name="my-telephony-agent",
+ room=f"outbound-{''.join(str(random.randint(0, 9)) for _ in range(10))}",
+ metadata='{"phone_number": "+16467085301"}', # HARDCODED
+ )
+ )
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=CallerAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint, agent_name="mcp-agent"))
diff --git a/environments/community/router_env/engine/agents/contact_agent.py b/environments/community/router_env/engine/agents/contact_agent.py
new file mode 100644
index 00000000..674ca92b
--- /dev/null
+++ b/environments/community/router_env/engine/agents/contact_agent.py
@@ -0,0 +1,84 @@
+import logging
+import os
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+ mcp,
+)
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("contact-agent")
+
+
+class ContactAgent(Agent):
+ def __init__(
+ self, chat_ctx: ChatContext, tools: Optional[List[function_tool]] = None
+ ) -> None:
+
+ final_instructions = (
+ "You are a Contact specialist. You can help find contact information such as phone "
+ "numbers, email addresses, or other details for individuals. "
+ + "You can also add new contacts or update existing ones if tools like "
+ "'get_contact_details', 'add_contact', 'update_contact' are available. "
+ + "If your task is complete or the user asks for something outside your contact "
+ "management capabilities (e.g., math, web search), you MUST use the "
+ "'delegate_to_router_agent' tool to return to the main assistant."
+ )
+
+ all_tools = tools if tools is not None else []
+
+ mcp_servers_list = []
+ zapier_mcp_url = os.getenv("ZAPIER_CONTACT_MCP_URL")
+ if zapier_mcp_url:
+ mcp_servers_list.append(
+ mcp.MCPServerHTTP(
+ url=zapier_mcp_url,
+ timeout=5,
+ client_session_timeout_seconds=5,
+ )
+ )
+ else:
+ logger.warning(
+ "ZAPIER_CONTACT_MCP_URL not set. Contact agent may not have all its tools."
+ )
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ mcp_servers=mcp_servers_list,
+ tools=all_tools,
+ )
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="alloy"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=ContactAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/gmail_agent.py b/environments/community/router_env/engine/agents/gmail_agent.py
new file mode 100644
index 00000000..a8ea8835
--- /dev/null
+++ b/environments/community/router_env/engine/agents/gmail_agent.py
@@ -0,0 +1,84 @@
+import logging
+import os
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+ mcp,
+)
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("gmail-agent")
+
+
+class GmailAgent(Agent):
+ def __init__(
+ self, chat_ctx: ChatContext, tools: Optional[List[function_tool]] = None
+ ) -> None:
+
+ final_instructions = (
+ "You are a Gmail specialist. You can manage emails by reading, searching, sending, and "
+ "updating them (e.g., marking as read/unread, moving to folders). "
+ + "Use tools like 'read_emails', 'send_email', and 'update_email' to interact with Gmail. "
+ + "If sending an email, you might need a recipient; you know Gabin (gabin.fay@gmail.com). "
+ + "If your task is complete or the user asks for something outside your email management "
+ "capabilities (e.g., math, calendar), "
+ + "you MUST use the 'delegate_to_router_agent' tool to return to the main assistant."
+ )
+
+ all_tools = tools if tools is not None else []
+
+ mcp_servers_list = []
+ gumloop_mcp_url = os.getenv("GUMLOOP_GMAIL_MCP_URL")
+ if gumloop_mcp_url:
+ mcp_servers_list.append(
+ mcp.MCPServerHTTP(
+ url=gumloop_mcp_url,
+ timeout=5,
+ client_session_timeout_seconds=5,
+ )
+ )
+ else:
+ logger.warning(
+ "GUMLOOP_GMAIL_MCP_URL not set. Gmail agent may not have all its tools."
+ )
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ mcp_servers=mcp_servers_list,
+ tools=all_tools,
+ )
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="alloy"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=GmailAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/go_agent.py b/environments/community/router_env/engine/agents/go_agent.py
new file mode 100644
index 00000000..98cd9094
--- /dev/null
+++ b/environments/community/router_env/engine/agents/go_agent.py
@@ -0,0 +1,187 @@
+import logging
+import os
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit.agents import JobContext, WorkerOptions, cli, function_tool, mcp
+from livekit.agents.llm import ChatChunk, ChatContext
+from livekit.agents.voice import Agent, AgentSession
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+logger = logging.getLogger("go-agent-livekit")
+logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+
+# Load environment variables from .env file
+load_dotenv()
+
+OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
+GOOGLE_MAPS_API_KEY = os.environ.get("GOOGLE_MAPS_API_KEY")
+DEEPGRAM_API_KEY = os.environ.get("DEEPGRAM_API_KEY")
+
+if not OPENAI_API_KEY:
+ logger.critical("🔴 CRITICAL: OPENAI_API_KEY not found. OpenAI plugins will fail.")
+
+if not GOOGLE_MAPS_API_KEY:
+ logger.critical(
+ "🔴 CRITICAL: GOOGLE_MAPS_API_KEY not found. Google Maps MCP server will fail."
+ )
+
+if not DEEPGRAM_API_KEY:
+ logger.warning(
+ "⚠️ WARNING: DEEPGRAM_API_KEY not found. Deepgram STT plugin may have issues."
+ )
+
+
+mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "google-maps",
+ "dist",
+ "index.js",
+ )
+)
+
+if not os.path.exists(mcp_script_path):
+ logger.critical(
+ f"CRITICAL: Google Maps MCP script not found at {mcp_script_path}. Agent cannot start tools."
+ )
+
+
+class GoAgent(Agent):
+ """A LiveKit agent specialized in location-based queries using Google Maps via MCP."""
+
+ def __init__(
+ self, chat_ctx: ChatContext, tools: Optional[List[function_tool]] = None
+ ):
+
+ final_instructions = (
+ "You are the Go Agent, specialized in providing location-based information using Google Maps. "
+ "You MUST use the available tools to fulfill user queries about locations, directions, "
+ "distances, and places.\n\n"
+ "RULE FOR LOCATION REQUESTS: When a user asks about finding a location, getting directions, "
+ "calculating distances, or information about a place, you MUST use the appropriate Google Maps tool.\n\n"
+ "Key tools available to you (provided by Google Maps MCP):\n"
+ "- maps_geocode: Convert an address to coordinates "
+ '(e.g., maps_geocode address="1600 Amphitheatre Parkway, Mountain View, CA")\n'
+ "- maps_reverse_geocode: Convert coordinates to an address "
+ "(e.g., maps_reverse_geocode latitude=37.422 longitude=-122.084)\n"
+ '- maps_search_places: Search for places (e.g., maps_search_places query="restaurants in London")\n'
+ "- maps_place_details: Get details for a place_id "
+ '(e.g., maps_place_details place_id="ChIJN1t_tDeuEmsRUsoyG83frY4")\n'
+ "- maps_directions: Get directions "
+ '(e.g., maps_directions origin="San Francisco" destination="Los Angeles" mode="driving")\n'
+ "- maps_distance_matrix: Calculate distances "
+ '(e.g., maps_distance_matrix origins="New York,Washington D.C." '
+ 'destinations="Boston,Philadelphia" mode="...")\n\n'
+ "RULE FOR TOOL RESULTS: After you receive results from a tool, you MUST analyze the data and "
+ "provide a clear, helpful response. Format addresses and directions in a readable way, "
+ "extract key information from place details, and always provide context for coordinates and distances.\n\n"
+ "If a tool call fails or returns no relevant information, explain clearly to the user and "
+ "suggest alternatives. "
+ "If your task is complete or the user asks for something outside your location/maps capabilities "
+ "(e.g., math, calendar), you MUST use the 'delegate_to_router_agent' tool to return to the main assistant."
+ )
+
+ all_tools = tools if tools is not None else []
+
+ mcp_servers_list = []
+ if GOOGLE_MAPS_API_KEY and os.path.exists(mcp_script_path):
+ mcp_servers_list.append(
+ mcp.MCPServerStdio(
+ command="node",
+ args=[mcp_script_path],
+ env={"GOOGLE_MAPS_API_KEY": GOOGLE_MAPS_API_KEY},
+ )
+ )
+ else:
+ logger.warning(
+ "Google Maps MCP server not configured due to missing API key or script path."
+ )
+
+ super().__init__(
+ instructions=final_instructions,
+ allow_interruptions=True,
+ chat_ctx=chat_ctx,
+ mcp_servers=mcp_servers_list,
+ tools=all_tools,
+ )
+ if not self.llm:
+ logger.error(
+ "GoAgentLivekit initialized, but LLM might be missing if API key was not "
+ "provided to plugin."
+ )
+
+ async def llm_node(self, chat_ctx: ChatContext, tools: list, model_settings: dict):
+ """Override the llm_node to log tool calls or add custom behavior."""
+ tool_call_detected_this_turn = False
+ async for chunk in super().llm_node(chat_ctx, tools, model_settings):
+ if (
+ isinstance(chunk, ChatChunk)
+ and chunk.delta
+ and chunk.delta.tool_calls
+ and not tool_call_detected_this_turn
+ ):
+ tool_call_detected_this_turn = True
+ logger.info(
+ "GoAgentLivekit: LLM is attempting to call a tool. Informing user."
+ )
+ if hasattr(self, "session") and self.session is not None:
+ self.session.say("Okay, let me check that for you.")
+ else:
+ logger.warning(
+ "Agent has no session to 'say' through during tool call detection."
+ )
+ yield chunk
+
+ async def on_enter(self):
+ # when the agent is added to the session, we'll initiate the conversation by
+ # using the LLM to generate a reply
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ """Main entrypoint for the LiveKit Go Agent application."""
+ logger.info(
+ f"Go Agent LiveKit starting entrypoint for Job ID: {getattr(ctx.job, 'id', 'unknown')}"
+ )
+
+ await ctx.connect()
+ logger.info(
+ f"Successfully connected to LiveKit room: {ctx.room.name if ctx.room else 'N/A'}"
+ )
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(
+ model="nova-2", language="en-US", api_key=os.environ.get("DEEPGRAM_API_KEY")
+ ),
+ llm=openai.LLM(model="gpt-4o", api_key=OPENAI_API_KEY),
+ tts=openai.TTS(voice="alloy", api_key=OPENAI_API_KEY),
+ turn_detection=MultilingualModel(),
+ )
+ logger.info("AgentSession configured with Google Maps MCP server.")
+
+ agent = GoAgent(chat_ctx=session._chat_ctx)
+ logger.info("GoAgentLivekit instantiated.")
+
+ logger.info(
+ f"Starting AgentSession with agent for room: {ctx.room.name if ctx.room else 'N/A'}"
+ )
+ await session.start(agent=agent, room=ctx.room)
+ logger.info("AgentSession started. GoAgentLivekit is now running.")
+
+
+if __name__ == "__main__":
+ logger.info("Starting Go Agent LiveKit application via cli.run_app.")
+ if not os.environ.get("DEEPGRAM_API_KEY"):
+ logger.warning(
+ "DEEPGRAM_API_KEY not found in environment. STT plugin may fail."
+ )
+
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/listen_agent.py b/environments/community/router_env/engine/agents/listen_agent.py
new file mode 100644
index 00000000..f41a9f91
--- /dev/null
+++ b/environments/community/router_env/engine/agents/listen_agent.py
@@ -0,0 +1,206 @@
+import logging
+import os
+import sys
+from typing import List, Optional
+
+from dotenv import load_dotenv
+from livekit.agents import (
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+ mcp,
+ tts,
+)
+from livekit.agents.llm import ChatChunk
+from livekit.agents.types import NOT_GIVEN
+from livekit.agents.voice import Agent, AgentSession
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+from pydantic import BaseModel, Field
+
+logger = logging.getLogger("agent-spotify-official")
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+_this_file_dir = os.path.dirname(os.path.abspath(__file__))
+_stone_ui_dir = os.path.abspath(os.path.join(_this_file_dir, "..", ".."))
+if _stone_ui_dir not in sys.path:
+ sys.path.insert(0, _stone_ui_dir)
+
+# Removed ANTHROPIC_API_KEY check as it seems unrelated to this OpenAI-based agent.
+
+# --- Spotify Tool Input Models (Based on spotify-mcp-server README) ---
+
+
+class PlayMusicInput(BaseModel):
+ uri: Optional[str] = Field(
+ None,
+ description="Spotify URI of the item to play (e.g., spotify:track:...). Overrides type and id.",
+ )
+ type: Optional[str] = Field(
+ None, description="Type of item to play (track, album, artist, playlist)"
+ )
+ id: Optional[str] = Field(None, description="Spotify ID of the item to play")
+ deviceId: Optional[str] = Field(
+ None, description="ID of the device to play on (optional)"
+ )
+
+
+# Add other input models here as needed (e.g., SearchSpotifyInput, PlaylistInput etc.)
+
+# --- Configure Spotify MCP Server ---
+spotify_mcp_server = None
+
+# Define the path to the BUILT MCP server script
+# IMPORTANT: Ensure the MCP server is built (npm run build) and authenticated (npm run auth)
+mcp_script_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__), "..", "tools", "mcp", "spotify", "build", "index.js"
+ )
+)
+spotify_config_path = os.path.abspath(
+ os.path.join(
+ os.path.dirname(__file__),
+ "..",
+ "tools",
+ "mcp",
+ "spotify",
+ "spotify-config.json",
+ )
+)
+
+if not os.path.exists(mcp_script_path):
+ logger.error(
+ f"❌ Spotify MCP script not found at {mcp_script_path}. Make sure you've run "
+ "'npm install && npm run build' in the server directory."
+ )
+ logger.warning("⚠️ Spotify tools will be unavailable.")
+elif not os.path.exists(spotify_config_path):
+ logger.error(
+ f"❌ Spotify config file not found at {spotify_config_path}. Make sure you've run "
+ "'npm run auth' after setting credentials."
+ )
+ logger.warning("⚠️ Spotify tools will likely be unavailable due to missing auth.")
+else:
+ # Check if config contains tokens (basic check)
+ try:
+ with open(spotify_config_path, "r") as f:
+ config_content = f.read()
+ if (
+ "accessToken" not in config_content
+ or "refreshToken" not in config_content
+ or "run-npm auth" in config_content
+ ):
+ logger.warning(
+ f"⚠️ Spotify config file at {spotify_config_path} seems incomplete or "
+ "unauthenticated. Run 'npm run auth'."
+ )
+ # We still configure the server, but it might fail at runtime
+ else:
+ logger.info("✅ Spotify config file seems authenticated.")
+ except Exception as e:
+ logger.error(f"Error reading Spotify config {spotify_config_path}: {e}")
+
+ logger.info(f"📂 Configuring Spotify MCP server with script: {mcp_script_path}")
+ spotify_mcp_server = mcp.MCPServerStdio(
+ "node", # Command to run the server
+ args=[mcp_script_path], # Argument is the script path
+ # No specific env vars needed here, reads from spotify-config.json
+ env={},
+ client_session_timeout_seconds=5 * 60,
+ )
+ logger.info("✅ Spotify MCP Server configured (runtime auth check still needed).")
+
+
+class ListenAgent(Agent):
+ """A LiveKit agent that uses MCP tools from one or more MCP servers."""
+
+ def __init__(
+ self,
+ chat_ctx: ChatContext,
+ instructions: Optional[str] = None,
+ tts: Optional[tts.TTS] = NOT_GIVEN,
+ tools: Optional[List[function_tool]] = None,
+ ):
+
+ final_instructions = (
+ instructions
+ if instructions is not None
+ else (
+ "You are the Listen Agent, specialized in controlling Spotify music playback. "
+ + "You MUST use the available tools to fulfill user requests related to Spotify. "
+ + "Available tools include 'playMusic', and potentially others like 'searchSpotify', "
+ "'pausePlayback', etc.\n\n"
+ + "RULE FOR MUSIC REQUESTS: When a user asks to play music, search for music, "
+ "control playback (pause, skip, etc.), manage playlists, or ask what's playing, "
+ "you MUST use the appropriate Spotify tool (like 'playMusic'). "
+ + "Be precise with parameters like 'uri' or 'type' and 'id'. "
+ "Infer parameters from the user query. If essential info is missing "
+ "(like what to play), ask the user.\n\n"
+ + "RULE FOR TOOL RESULTS: After a tool is successfully executed, you MUST confirm "
+ "the action to the user "
+ "(e.g., 'Okay, playing 'Bohemian Rhapsody' now.'). "
+ + "If a tool fails or returns an error, inform the user clearly. "
+ + "If your task is complete or the user asks for something outside your Spotify capabilities "
+ "(e.g., math, calendar), you MUST use the 'delegate_to_router_agent' tool to return to the "
+ "main assistant."
+ )
+ )
+
+ all_tools = tools if tools is not None else []
+
+ active_mcp_servers = []
+ if spotify_mcp_server is not None:
+ active_mcp_servers.append(spotify_mcp_server)
+
+ super().__init__(
+ instructions=final_instructions,
+ chat_ctx=chat_ctx,
+ allow_interruptions=True,
+ mcp_servers=active_mcp_servers, # MODIFIED: Pass filtered list
+ tools=all_tools, # Pass the tools to the parent Agent class
+ )
+
+ async def llm_node(self, chat_ctx, tools, model_settings):
+ """Override the llm_node to say a message when a tool call is detected."""
+ tool_call_detected = False
+
+ async for chunk in super().llm_node(chat_ctx, tools, model_settings):
+ if (
+ isinstance(chunk, ChatChunk)
+ and chunk.delta
+ and chunk.delta.tool_calls
+ and not tool_call_detected
+ ):
+ tool_call_detected = True
+ # Use self.session.say() to make the agent speak, only if TTS is configured
+ if self.tts: # Check if the agent has a TTS instance
+ self.session.say("Sure, I'll check that for you.")
+ yield chunk
+
+ async def on_enter(self):
+ # when the agent is added to the session, we'll initiate the conversation by
+ # using the LLM to generate a reply
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ """Main entrypoint for the LiveKit agent application."""
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-2", language="en-US"),
+ llm=openai.LLM(model="gpt-4o"),
+ tts=openai.TTS(voice="alloy"),
+ turn_detection=MultilingualModel(),
+ )
+
+ agent = ListenAgent(chat_ctx=session._chat_ctx)
+ await session.start(agent=agent, room=ctx.room)
+
+
+if __name__ == "__main__":
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
diff --git a/environments/community/router_env/engine/agents/mem_agent.py b/environments/community/router_env/engine/agents/mem_agent.py
new file mode 100644
index 00000000..0172328e
--- /dev/null
+++ b/environments/community/router_env/engine/agents/mem_agent.py
@@ -0,0 +1,252 @@
+import logging
+from pathlib import Path
+
+from dotenv import load_dotenv
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ JobContext,
+ JobProcess,
+ WorkerOptions,
+ cli,
+ llm,
+)
+from livekit.plugins import deepgram, openai, silero
+from mem0 import AsyncMemoryClient
+
+# Load environment variables
+load_dotenv(dotenv_path=Path(__file__).parent.parent.parent / ".env")
+
+# Configure logging
+logger = logging.getLogger("memory-assistant")
+logger.setLevel(logging.INFO)
+logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+)
+
+# Define a global user ID for simplicity
+USER_ID = "voice_user"
+
+# Initialize Mem0 memory client
+mem0 = AsyncMemoryClient()
+
+
+async def _enrich_with_memory(
+ last_user_msg: llm.ChatMessage, chat_ctx_to_modify: llm.ChatContext
+):
+ """Add memories and Augment chat context with relevant memories"""
+ if (
+ not last_user_msg
+ or not last_user_msg.text_content
+ or not last_user_msg.text_content.strip()
+ ):
+ logger.info("No valid last user message content to process for memory.")
+ return
+
+ try:
+ # Ensure last_user_msg.text_content is a string for mem0
+ content_str = last_user_msg.text_content
+ if not content_str or not content_str.strip():
+ logger.info("User message content is empty after getting text_content.")
+ return
+
+ logger.info(
+ f"[Mem0] Attempting to add memory for USER_ID '{USER_ID}': '{content_str}'"
+ )
+ try:
+ add_response = await mem0.add(
+ [{"role": "user", "content": content_str}], user_id=USER_ID
+ )
+ logger.info(f"[Mem0] Successfully added memory. Response: {add_response}")
+ except Exception as e:
+ logger.error(f"[Mem0] Error adding memory: {e}", exc_info=True)
+ # Decide if we should return or continue to search with potentially stale memory
+ # For now, we'll continue to search.
+
+ logger.info(
+ f"[Mem0] Attempting to search memories for USER_ID '{USER_ID}' with query: '{content_str}'"
+ )
+ results = []
+ try:
+ results = await mem0.search(
+ content_str,
+ user_id=USER_ID,
+ )
+ logger.info(
+ f"[Mem0] Search complete. Found {len(results)} results: {results}"
+ )
+ except Exception as e:
+ logger.error(f"[Mem0] Error searching memory: {e}", exc_info=True)
+
+ if results:
+ memories_text = " ".join(
+ [result["memory"] for result in results if result.get("memory")]
+ )
+ if memories_text.strip():
+ logger.info(f"Enriching with memory: {memories_text}")
+
+ # Create the RAG message. Ensure content is a list of ChatContent (string is fine).
+ rag_msg_content = (
+ f"Relevant Memory from past interactions: {memories_text}\\n"
+ "User's current query is below."
+ )
+ rag_msg = llm.ChatMessage(role="system", content=[rag_msg_content])
+
+ # Insert RAG message before the last user message in the context's items list
+ inserted = False
+ # Access items via the .items property
+ target_items_list = chat_ctx_to_modify.items
+ for i in range(len(target_items_list) - 1, -1, -1):
+ if target_items_list[i] is last_user_msg: # Check object identity
+ target_items_list.insert(i, rag_msg)
+ inserted = True
+ logger.info(f"Inserted RAG message at index {i} in .items list")
+ break
+
+ if not inserted:
+ logger.warning(
+ "Could not find last user message by identity in .items list. "
+ "Appending RAG message."
+ )
+ if target_items_list and target_items_list[-1] is last_user_msg:
+ target_items_list.insert(len(target_items_list) - 1, rag_msg)
+ else:
+ target_items_list.append(rag_msg)
+
+ except Exception as e:
+ logger.error(f"Error during memory enrichment: {e}", exc_info=True)
+
+
+class MemoryAgent(Agent):
+ def __init__(self, chat_ctx: llm.ChatContext):
+ super().__init__(
+ chat_ctx=chat_ctx,
+ instructions="You are a helpful voice assistant that can remember past interactions.",
+ )
+ # System prompt is now managed by the chat_ctx passed to super().__init__
+
+ async def on_enter(self):
+ logger.info("MemoryAgent entered room.")
+ try:
+ # Say initial greeting
+ await self.session.say(
+ "Hello! I'm George. Can I help you plan an upcoming trip? ",
+ allow_interruptions=True,
+ )
+ # Start the main interaction loop
+ self.session.generate_reply()
+ logger.info("MemoryAgent started generate_reply loop.")
+ except Exception as e:
+ logger.error(f"Error in MemoryAgent.on_enter: {e}", exc_info=True)
+
+ async def on_user_turn_completed(
+ self, turn_ctx: llm.ChatContext, new_message: llm.ChatMessage
+ ):
+ logger.info(
+ f"MemoryAgent.on_user_turn_completed called with new_message: '{new_message.text_content}'"
+ )
+
+ if (
+ not new_message
+ or not new_message.content
+ or not new_message.text_content.strip()
+ ):
+ logger.info("No valid new_message content for memory enrichment.")
+ return
+
+ # The turn_ctx provided by the hook is the context *before* the new_message.
+ # We need to add the new_message to it before enrichment,
+ # so _enrich_with_memory can potentially place the RAG message *before* it.
+ # The AgentActivity will use this modified turn_ctx for the LLM call.
+ # It will also separately add the new_message to the agent's main context.
+
+ # Let's make a working copy if direct modification isn't intended for the passed turn_ctx,
+ # though the name temp_mutable_chat_ctx in AgentActivity suggests it's okay.
+ # For safety and clarity in _enrich_with_memory, we'll operate on turn_ctx.
+
+ # Add the new user message to the context that will be enriched
+ turn_ctx.items.append(
+ new_message
+ ) # new_message is already part of the main context by AgentActivity
+ # but for _enrich_with_memory to find it (as last_user_msg)
+ # and insert RAG before it in *this specific context copy*, it needs to be here.
+ # AgentActivity also adds this new_message to the agent's _chat_ctx separately.
+
+ logger.info(
+ f"Context before enrichment (with new_message added): {turn_ctx.items}"
+ )
+
+ # Enrich the context (which now includes new_message) with memories
+ # _enrich_with_memory will find new_message as the last user message
+ # and insert the RAG system message just before it in turn_ctx.items
+ await _enrich_with_memory(new_message, turn_ctx)
+
+ logger.info(f"Context after enrichment: {turn_ctx.items}")
+ # No need to call self.update_chat_ctx() here.
+ # The AgentActivity will use the modified turn_ctx for the LLM.
+
+
+def prewarm_process(proc: JobProcess):
+ logger.info("Prewarming VAD model.")
+ # Preload silero VAD in memory to speed up session start
+ proc.userdata["vad"] = silero.VAD.load()
+ logger.info("VAD model prewarmed.")
+
+
+async def entrypoint(ctx: JobContext):
+ logger.info("Agent entrypoint started.")
+ try:
+ await ctx.connect()
+ logger.info("Connected to LiveKit room.")
+
+ # Define initial system context for the LLM
+ initial_ctx = llm.ChatContext()
+ system_prompt_text = """
+ You are a helpful voice assistant.
+ You are a travel guide named George and will help the user to plan a travel trip of their dreams.
+ You should help the user plan for various adventures like work retreats, family vacations or
+ solo backpacking trips.
+ You should be careful to not suggest anything that would be dangerous, illegal or inappropriate.
+ You can remember past interactions and use them to inform your answers.
+ Use semantic memory retrieval to provide contextually relevant responses.
+ When relevant memory is provided, use it to enhance your response.
+ """
+ initial_ctx.add_message(role="system", content=system_prompt_text)
+ logger.info("Initial system context defined.")
+
+ # VAD model loading logic remains the same
+ vad_model = ctx.proc.userdata.get("vad")
+ if not vad_model:
+ logger.info("VAD not prewarmed or not found in userdata, loading now.")
+ vad_model = silero.VAD.load()
+ else:
+ logger.info("Using prewarmed VAD model.")
+
+ custom_agent = MemoryAgent(chat_ctx=initial_ctx)
+
+ # AgentSession constructor does NOT take 'agent'
+ session = AgentSession(
+ vad=vad_model,
+ stt=deepgram.STT(model="nova-2", language="en"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="alloy"),
+ )
+ logger.info("AgentSession created.")
+
+ # Agent is passed to session.start()
+ await session.start(agent=custom_agent, room=ctx.room)
+ logger.info("Agent session started with MemoryAgent.")
+
+ except Exception as e:
+ logger.error(f"Error in agent entrypoint: {e}", exc_info=True)
+
+
+# Run the application
+if __name__ == "__main__":
+ cli.run_app(
+ WorkerOptions(
+ entrypoint_fnc=entrypoint,
+ prewarm_fnc=prewarm_process,
+ agent_name="mem0-voice-agent",
+ )
+ ) # Consistent agent name
diff --git a/environments/community/router_env/engine/agents/router_agent.py b/environments/community/router_env/engine/agents/router_agent.py
new file mode 100644
index 00000000..3316e2c9
--- /dev/null
+++ b/environments/community/router_env/engine/agents/router_agent.py
@@ -0,0 +1,272 @@
+import logging
+import os
+import sys # Import sys for sys.exit
+from pathlib import Path
+
+# Import the original FunctionAgents from the official agent files
+# These files should be in the same directory as router_agent.py
+from ask_agent import AskAgent
+from calc_agent import CalculatorAgent
+from calendar_agent import CalendarAgent
+from caller_agent import CallerAgent
+from contact_agent import ContactAgent
+from dotenv import load_dotenv
+from gmail_agent import GmailAgent
+from go_agent import GoAgent
+from listen_agent import ListenAgent
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ RunContext,
+ WorkerOptions,
+ cli,
+ function_tool,
+)
+from livekit.plugins import openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+# from mem_agent import MemoryAgent
+
+load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")
+
+logger = logging.getLogger("router-agent")
+
+# Determine the absolute path for server scripts relative to this file
+_current_dir = os.path.dirname(os.path.abspath(__file__))
+
+
+@function_tool
+async def delegate_to_router_agent(
+ context: RunContext,
+ original_query: str = "User wants to talk about something else.",
+):
+ """
+ Call this function to delegate the conversation back to the main RouterAgent.
+ This is used when your current task is complete, or the user asks for functionality
+ that you (the specialist agent) do not provide.
+ Args:
+ original_query: A brief description of why the delegation is happening or the user's last relevant query.
+ """
+ logger.info(
+ f"Specialist Agent: Delegating back to RouterAgent. Reason/Query: '{original_query}'"
+ )
+ # Try to access _chat_ctx via context.session, as context.agent was problematic
+ if not hasattr(context, "session") or context.session is None:
+ logger.error(
+ "delegate_to_router_agent: RunContext does not have a valid 'session' attribute."
+ )
+ # This is a critical failure for context propagation.
+ # Depending on desired behavior, could raise an error or attempt a recovery (though recovery is hard here).
+ # For now, we'll let it fail if it tries to access _chat_ctx on a None session,
+ # or re-raise a more specific error.
+ raise AttributeError(
+ "RunContext is missing the session attribute, cannot retrieve ChatContext."
+ )
+
+ return (
+ RouterAgent(chat_ctx=context.session._chat_ctx),
+ "Okay, let me switch you back to the main assistant.",
+ )
+
+
+class RouterAgent(Agent):
+ """Routes user queries to specialized agents."""
+
+ def __init__(self, chat_ctx: ChatContext):
+ super().__init__(
+ instructions="""
+ You are a router agent. Your primary responsibility is to understand the user's voice query
+ and delegate it to the most appropriate specialist agent.
+ - If the query is primarily about mathematics, calculations, arithmetic, or numbers,
+ you MUST use the 'delegate_to_math_agent' tool.
+ - For general knowledge questions, facts, explanations, requests to 'search the web',
+ 'make a web search', or any other type of query not strictly mathematical, not about
+ specific addresses/locations, and not covered by other specialists,
+ you MUST use the 'delegate_to_perplexity_agent' tool.
+ - If the query involves calendar events, scheduling, creating appointments, or asking
+ about your schedule, you MUST use the 'delegate_to_calendar_agent' tool.
+ - If the user explicitly asks to make a phone call,
+ you MUST use the 'delegate_to_caller_agent' tool.
+ - If the query is about finding contact information (like phone numbers or email
+ addresses of people), you MUST use the 'delegate_to_contact_agent' tool.
+ - For tasks related to managing emails (reading, sending, searching Gmail),
+ you MUST use the 'delegate_to_gmail_agent' tool.
+ - If the query is about locations, finding places, getting directions, looking up
+ addresses, or anything map-related, you MUST use the 'delegate_to_go_agent' tool.
+ - If the user wants to play music, control music playback, or anything related to Spotify,
+ you MUST use the 'delegate_to_listen_agent' tool.
+ Listen carefully to the user's query and make a clear decision.
+ Do not attempt to answer the question yourself. Your sole job is to route.
+ If uncertain, you can ask one clarifying question to determine the correct agent,
+ but prefer to route directly if possible.
+ """,
+ allow_interruptions=True,
+ chat_ctx=chat_ctx,
+ )
+
+ async def on_enter(self):
+ """Called when the RouterAgent starts. It will wait for user input."""
+ logger.info("RouterAgent entered. Waiting for user query.")
+ self.session.generate_reply()
+
+ @function_tool
+ async def delegate_to_math_agent(self, query: str):
+ """
+ Call this function to delegate a math-related query to the MathSpecialistAgent.
+ Args:
+ query: The user's original voice query that is mathematical in nature.
+ """
+ logger.info(
+ f"RouterAgent: Delegating to MathSpecialistAgent for query: '{query}'"
+ )
+ # Pass the delegate_to_router_agent tool to the CalculatorAgent
+ math_agent = CalculatorAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return math_agent, "Okay, I'll connect you with my math specialist for that."
+
+ @function_tool
+ async def delegate_to_perplexity_agent(self, query: str):
+ """
+ Call this function to delegate a query that needs to perform a web search to the Perplexity Agent.
+ Args:
+ query: The user's original voice query.
+ """
+ logger.info(
+ f"RouterAgent: Delegating to AskAgent (for perplexity tasks) for query: '{query}'"
+ )
+ try:
+ perplexity_agent = AskAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return (
+ perplexity_agent,
+ "Alright, let me get my knowledge expert to help with that question.",
+ )
+ except AttributeError as e:
+ logger.error(f"Unexpected AttributeError: {e}")
+ raise
+
+ @function_tool
+ async def delegate_to_calendar_agent(self, query: str):
+ """
+ Call this function to delegate a query about calendar events, scheduling, or appointments to the CalendarAgent.
+ Args:
+ query: The user's original voice query related to calendar.
+ """
+ logger.info(f"RouterAgent: Delegating to CalendarAgent for query: '{query}'")
+ calendar_agent = CalendarAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return calendar_agent, "Okay, let me check your calendar."
+
+ @function_tool
+ async def delegate_to_caller_agent(self, query: str):
+ """
+ Call this function to delegate a request to make a phone call to the CallerAgent.
+ Args:
+ query: The user's original voice query about making a call.
+ """
+ logger.info(f"RouterAgent: Delegating to CallerAgent for query: '{query}'")
+ caller_agent = CallerAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return caller_agent, "Sure, I can try to make that call for you."
+
+ @function_tool
+ async def delegate_to_contact_agent(self, query: str):
+ """
+ Call this function to delegate a query about finding or managing contact information to the ContactAgent.
+ Args:
+ query: The user's original voice query related to contacts.
+ """
+ logger.info(f"RouterAgent: Delegating to ContactAgent for query: '{query}'")
+ contact_agent = ContactAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return contact_agent, "Let me look up that contact information for you."
+
+ @function_tool
+ async def delegate_to_gmail_agent(self, query: str):
+ """
+ Call this function to delegate an email-related query (reading, sending, managing emails) to the GmailAgent.
+ Args:
+ query: The user's original voice query related to Gmail.
+ """
+ logger.info(f"RouterAgent: Delegating to GmailAgent for query: '{query}'")
+ gmail_agent = GmailAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return gmail_agent, "Okay, I'll check your emails."
+
+ @function_tool
+ async def delegate_to_go_agent(self, query: str):
+ """
+ Call this function to delegate a query about locations, directions, maps, or places to the GoAgent.
+ Args:
+ query: The user's original voice query related to maps or navigation.
+ """
+ logger.info(f"RouterAgent: Delegating to GoAgent for query: '{query}'")
+ go_agent = GoAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return go_agent, "Let me get my navigation expert for that."
+
+ @function_tool
+ async def delegate_to_listen_agent(self, query: str):
+ """
+ Call this function to delegate a request to play or control music (Spotify) to the ListenAgent.
+ Args:
+ query: The user's original voice query related to music or Spotify.
+ """
+ logger.info(f"RouterAgent: Delegating to ListenAgent for query: '{query}'")
+ listen_agent = ListenAgent(
+ chat_ctx=self.session._chat_ctx,
+ tools=[delegate_to_router_agent], # Pass the tool
+ )
+ return listen_agent, "Okay, let's get some music playing."
+
+
+async def entrypoint(ctx: JobContext):
+ """Main entrypoint for the multi-agent LiveKit application."""
+ await ctx.connect()
+ logger.info("Router agent connected to LiveKit.")
+
+ session = AgentSession[None](
+ vad=silero.VAD.load(),
+ stt=openai.STT(model="gpt-4o-mini-transcribe", detect_language=True),
+ tts=openai.TTS(voice="alloy", model="tts-1-hd"),
+ llm=openai.LLM(model="gpt-4o"),
+ turn_detection=MultilingualModel(),
+ )
+ logger.info(
+ "AgentSession configured. MCP servers will be managed by individual specialist agents."
+ )
+
+ initial_agent = RouterAgent(chat_ctx=session._chat_ctx)
+ await session.start(agent=initial_agent, room=ctx.room)
+ logger.info("RouterAgent session started.")
+
+
+if __name__ == "__main__":
+ # Setup basic logging if running directly
+ logging.basicConfig(
+ level=logging.INFO,
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
+ )
+ try:
+ cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
+ except SystemExit: # Allow sys.exit() to pass through without logging as critical
+ raise
+ except Exception as e:
+ logger.critical(f"Unhandled exception at top level: {e}", exc_info=True)
+ sys.exit(1) # Ensure exit with error code
diff --git a/environments/community/router_env/engine/agents/telephony_agent.py b/environments/community/router_env/engine/agents/telephony_agent.py
new file mode 100644
index 00000000..4ba690f5
--- /dev/null
+++ b/environments/community/router_env/engine/agents/telephony_agent.py
@@ -0,0 +1,99 @@
+import logging
+import os
+from pathlib import Path
+
+from dotenv import load_dotenv
+from livekit import api
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ RunContext,
+ WorkerOptions,
+ cli,
+ get_job_context,
+)
+from livekit.agents.llm import function_tool
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+
+# Add this function definition anywhere
+async def hangup_call():
+ ctx = get_job_context()
+ if ctx is None:
+ # Not running in a job context
+ return
+
+ await ctx.api.room.delete_room(
+ api.DeleteRoomRequest(
+ room=ctx.room.name,
+ )
+ )
+
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("mcp-agent")
+
+load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")
+
+
+class MyAgent(Agent):
+ def __init__(self, chat_ctx: ChatContext) -> None:
+ super().__init__(
+ instructions=(
+ "You can have phone calls. The interface is voice-based: "
+ "accept spoken user queries and respond with synthesized speech."
+ ),
+ chat_ctx=chat_ctx,
+ )
+
+ @function_tool
+ async def end_call(self, ctx: RunContext):
+ """Called when the user wants to end the call"""
+ # let the agent finish speaking
+ current_speech = ctx.session.current_speech
+ if current_speech:
+ await current_speech.wait_for_playout()
+
+ await hangup_call()
+
+ @function_tool
+ async def end_call_finished_by_you(self, ctx: RunContext):
+ """Called when you have accomplished your task and can end the call safely"""
+ # let the agent finish speaking
+ current_speech = ctx.session.current_speech
+ if current_speech:
+ await current_speech.wait_for_playout()
+
+ await hangup_call()
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=MyAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+ await session.generate_reply(
+ instructions="Greet the user and offer your assistance."
+ )
+
+
+if __name__ == "__main__":
+ cli.run_app(
+ WorkerOptions(entrypoint_fnc=entrypoint, agent_name="my-telephony-agent")
+ )
diff --git a/environments/community/router_env/engine/agents/telephony_agent_outbound.py b/environments/community/router_env/engine/agents/telephony_agent_outbound.py
new file mode 100644
index 00000000..a20e0538
--- /dev/null
+++ b/environments/community/router_env/engine/agents/telephony_agent_outbound.py
@@ -0,0 +1,98 @@
+import json
+import logging
+import os
+from pathlib import Path
+
+from dotenv import load_dotenv
+from livekit import api
+from livekit.agents import (
+ Agent,
+ AgentSession,
+ ChatContext,
+ JobContext,
+ WorkerOptions,
+ cli,
+)
+from livekit.plugins import deepgram, openai, silero
+from livekit.plugins.turn_detector.multilingual import MultilingualModel
+
+load_dotenv(os.path.join(os.path.dirname(__file__), "..", "..", ".env"))
+
+
+logger = logging.getLogger("mcp-agent")
+
+load_dotenv(dotenv_path=Path(__file__).parent.parent / ".env")
+
+
+class MyAgent(Agent):
+ def __init__(self, chat_ctx: ChatContext) -> None:
+ super().__init__(
+ instructions=(
+ "You can have phone calls. The interface is voice-based: "
+ "accept spoken user queries and respond with synthesized speech."
+ ),
+ chat_ctx=chat_ctx,
+ )
+
+ async def on_enter(self):
+ self.session.generate_reply()
+
+
+async def entrypoint(ctx: JobContext):
+ await ctx.connect()
+
+ # If a phone number was provided, then place an outbound call
+ # By having a condition like this, you can use the same agent for inbound/outbound telephony
+ # as well as web/mobile/etc.
+ dial_info = json.loads(ctx.job.metadata)
+ phone_number = dial_info["phone_number"]
+
+ # The participant's identity can be anything you want, but this example uses the phone number itself
+ sip_participant_identity = phone_number
+ if phone_number is not None:
+ # The outbound call will be placed after this method is executed
+ try:
+ await ctx.api.sip.create_sip_participant(
+ api.CreateSIPParticipantRequest(
+ # This ensures the participant joins the correct room
+ room_name=ctx.room.name,
+ # This is the outbound trunk ID to use (i.e. which phone number the call will come from)
+ # You can get this from LiveKit CLI with `lk sip outbound list`
+ sip_trunk_id=os.environ.get("TWILIO_SIP_TRUNK_ID"),
+ # The outbound phone number to dial and identity to use
+ sip_call_to=phone_number,
+ participant_identity=sip_participant_identity,
+ # This will wait until the call is answered before returning
+ wait_until_answered=True,
+ )
+ )
+
+ print("call picked up successfully")
+ except api.TwirpError as e:
+ print(
+ f"error creating SIP participant: {e.message}, "
+ f"SIP status: {e.metadata.get('sip_status_code')} "
+ f"{e.metadata.get('sip_status')}"
+ )
+ ctx.shutdown()
+
+ session = AgentSession(
+ vad=silero.VAD.load(),
+ stt=deepgram.STT(model="nova-3", language="multi"),
+ llm=openai.LLM(model="gpt-4o-mini"),
+ tts=openai.TTS(voice="ash"),
+ turn_detection=MultilingualModel(),
+ )
+
+ await session.start(agent=MyAgent(chat_ctx=session._chat_ctx), room=ctx.room)
+
+ if phone_number is None:
+ await session.generate_reply(
+ instructions="Greet the user and offer your assistance."
+ )
+
+
+if __name__ == "__main__":
+ cli.run_app(
+ WorkerOptions(entrypoint_fnc=entrypoint, agent_name="my-telephony-agent")
+ )
diff --git a/environments/community/router_env/engine/config/__init__.py b/environments/community/router_env/engine/config/__init__.py
new file mode 100644
index 00000000..32113d18
--- /dev/null
+++ b/environments/community/router_env/engine/config/__init__.py
@@ -0,0 +1 @@
+# This file makes Python treat the directory as a package.
diff --git a/environments/community/router_env/engine/config/settings.py b/environments/community/router_env/engine/config/settings.py
new file mode 100644
index 00000000..a0bf38d7
--- /dev/null
+++ b/environments/community/router_env/engine/config/settings.py
@@ -0,0 +1,15 @@
+from pydantic_settings import BaseSettings
+
+
+class Settings(BaseSettings):
+ # Add any specific settings required by the application here
+ # For example:
+ # spotify_client_id: str | None = None
+ # spotify_client_secret: str | None = None
+
+ class Config:
+ env_file = ".env"
+ env_file_encoding = "utf-8"
+
+
+settings = Settings()
diff --git a/environments/community/router_env/engine/pyproject.toml b/environments/community/router_env/engine/pyproject.toml
new file mode 100644
index 00000000..695ff430
--- /dev/null
+++ b/environments/community/router_env/engine/pyproject.toml
@@ -0,0 +1,74 @@
+[project]
+name = "engine"
+version = "0.1.0"
+description = "Backend server for Stone OS with Pydantic-AI agents"
+authors = [
+ { name = "Stone AIOS Team", email = "dev@example.com" }, # Placeholder author
+]
+dependencies = [
+ "fastapi>=0.103.1",
+ "uvicorn[standard]>=0.23.2", # Added [standard] for better performance
+ "pydantic>=2.4.2",
+ "pydantic-settings>=2.0.3",
+ "anthropic>=0.15.0",
+ "logfire>=0.19.0",
+ "httpx>=0.25.0", # Needed for tests and EmailEngine API communication
+ # asyncio is built-in
+ "python-dotenv>=1.0.0",
+ "typing_extensions>=4.8.0",
+ # Add missing dependencies for memory system
+ "aiofiles>=0.8.0",
+ "filelock>=3.0.0",
+ "apscheduler",
+ # Add scheduler for background tasks
+ # "apscheduler>=3.0.0,<4.0.0", # Added APScheduler V3
+ # MCP server dependency - assuming the Python SDK is needed if direct node execution fails
+ # 'mcp-sdk @ git+https://github.com/modelcontextprotocol/python-sdk.git', # Uncomment if needed
+ "spotipy>=2.0.0", # Added for Listen Agent Spotify integration
+ # Add Google API client libraries for Connect Agent direct integration
+ "google-api-python-client>=2.0.0",
+ "google-auth-oauthlib>=0.7.0",
+ "google-auth-httplib2", # Relaxed constraint
+
+ # --- Added LiveKit Agents ---
+ "livekit-agents[mcp]~=1.0",
+ "livekit-plugins-openai", # Example, if OpenAI is used for LLM/TTS/STT
+ "livekit-plugins-deepgram", # Example, if Deepgram is used for STT
+ "livekit-plugins-silero", # For VAD
+ "livekit-plugins-cartesia", # Example, for TTS
+ "livekit-plugins-turn-detector", # For turn detection
+ # ----------------------------
+]
+requires-python = ">=3.13" # Updated minimum Python version for mcp-gsuite
+readme = "README.md" # Assuming a README exists or will be created
+license = { text = "MIT" } # Or choose appropriate license
+
+# Add optional dependencies for development/testing
+[project.optional-dependencies]
+dev = [
+ "pytest>=7.0",
+ "pytest-asyncio>=0.20",
+ "respx>=0.20",
+ # Add other dev tools like linters (ruff, mypy) or formatters (black) here if desired
+]
+
+[project.urls]
+Homepage = "https://github.com/stone-ui/stone-aios" # Placeholder URL
+Repository = "https://github.com/stone-ui/stone-aios" # Placeholder URL
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+[tool.uv]
+# Optional: Specify settings for uv if needed
+
+[tool.hatch.metadata]
+allow-direct-references = true
+
+[tool.hatch.build.targets.wheel]
+# Explicitly include directories containing the package code relative to pyproject.toml
+# Assuming these contain the core library code. Adjust if needed.
+packages = ["engine/agents", "engine/api", "engine/config", "engine/tools"] # Adjusted paths
+# If main.py and __init__.py should also be part of the package, might need adjustment
+# e.g., include '.' or specific file paths if they are not just scripts.
diff --git a/environments/community/router_env/engine/requirements.txt b/environments/community/router_env/engine/requirements.txt
new file mode 100644
index 00000000..7076959f
--- /dev/null
+++ b/environments/community/router_env/engine/requirements.txt
@@ -0,0 +1,401 @@
+# This file was autogenerated by uv via the following command:
+# uv pip compile pyproject.toml -o requirements.txt
+aiofiles==24.1.0
+ # via
+ # engine (pyproject.toml)
+ # livekit
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.11.18
+
+livekit-agents[anthropic]~=1.0
+
+ # via
+ # livekit-agents
+ # livekit-api
+aiosignal==1.3.2
+ # via aiohttp
+annotated-types==0.7.0
+ # via pydantic
+anthropic==0.51.0
+ # via engine (pyproject.toml)
+anyio==4.9.0
+ # via
+ # anthropic
+ # httpx
+ # mcp
+ # openai
+ # sse-starlette
+ # starlette
+ # watchfiles
+apscheduler==3.11.0
+ # via engine (pyproject.toml)
+attrs==25.3.0
+ # via aiohttp
+av==14.3.0
+ # via livekit-agents
+cachetools==5.5.2
+ # via google-auth
+certifi==2025.4.26
+ # via
+ # httpcore
+ # httpx
+ # requests
+cffi==1.17.1
+ # via sounddevice
+charset-normalizer==3.4.2
+ # via requests
+click==8.2.0
+ # via
+ # livekit-agents
+ # uvicorn
+colorama==0.4.6
+ # via livekit-agents
+coloredlogs==15.0.1
+ # via onnxruntime
+deprecated==1.2.18
+ # via
+ # opentelemetry-api
+ # opentelemetry-exporter-otlp-proto-http
+ # opentelemetry-semantic-conventions
+distro==1.9.0
+ # via
+ # anthropic
+ # openai
+docstring-parser==0.16
+ # via livekit-agents
+eval-type-backport==0.2.2
+ # via livekit-agents
+executing==2.2.0
+ # via logfire
+fastapi==0.115.12
+ # via engine (pyproject.toml)
+filelock==3.18.0
+ # via
+ # engine (pyproject.toml)
+ # huggingface-hub
+ # transformers
+flatbuffers==25.2.10
+ # via onnxruntime
+frozenlist==1.6.0
+ # via
+ # aiohttp
+ # aiosignal
+fsspec==2025.3.2
+ # via huggingface-hub
+google-api-core==2.24.2
+ # via google-api-python-client
+google-api-python-client==2.169.0
+ # via engine (pyproject.toml)
+google-auth==2.40.1
+ # via
+ # google-api-core
+ # google-api-python-client
+ # google-auth-httplib2
+ # google-auth-oauthlib
+google-auth-httplib2==0.2.0
+ # via
+ # engine (pyproject.toml)
+ # google-api-python-client
+google-auth-oauthlib==1.2.2
+ # via engine (pyproject.toml)
+googleapis-common-protos==1.70.0
+ # via
+ # google-api-core
+ # opentelemetry-exporter-otlp-proto-http
+h11==0.16.0
+ # via
+ # httpcore
+ # uvicorn
+httpcore==1.0.9
+ # via httpx
+httplib2==0.22.0
+ # via
+ # google-api-python-client
+ # google-auth-httplib2
+httptools==0.6.4
+ # via uvicorn
+httpx==0.28.1
+ # via
+ # engine (pyproject.toml)
+ # anthropic
+ # mcp
+ # openai
+httpx-sse==0.4.0
+ # via mcp
+huggingface-hub==0.31.2
+ # via
+ # tokenizers
+ # transformers
+humanfriendly==10.0
+ # via coloredlogs
+idna==3.10
+ # via
+ # anyio
+ # httpx
+ # requests
+ # yarl
+importlib-metadata==8.6.1
+ # via opentelemetry-api
+jinja2==3.1.6
+ # via livekit-plugins-turn-detector
+jiter==0.9.0
+ # via
+ # anthropic
+ # openai
+livekit==1.0.7
+ # via livekit-agents
+livekit-agents==1.0.22
+ # via
+ # engine (pyproject.toml)
+ # livekit-plugins-cartesia
+ # livekit-plugins-deepgram
+ # livekit-plugins-openai
+ # livekit-plugins-silero
+ # livekit-plugins-turn-detector
+livekit-api==1.0.2
+ # via livekit-agents
+livekit-plugins-cartesia==1.0.21
+ # via engine (pyproject.toml)
+livekit-plugins-deepgram==1.0.21
+ # via engine (pyproject.toml)
+livekit-plugins-openai==1.0.21
+ # via engine (pyproject.toml)
+livekit-plugins-silero==1.0.21
+ # via engine (pyproject.toml)
+livekit-plugins-turn-detector==1.0.21
+ # via engine (pyproject.toml)
+livekit-protocol==1.0.3
+ # via
+ # livekit-agents
+ # livekit-api
+logfire==3.16.0
+ # via engine (pyproject.toml)
+markdown-it-py==3.0.0
+ # via rich
+markupsafe==3.0.2
+ # via jinja2
+mcp==1.9.0
+ # via livekit-agents
+mdurl==0.1.2
+ # via markdown-it-py
+mpmath==1.3.0
+ # via sympy
+multidict==6.4.3
+ # via
+ # aiohttp
+ # yarl
+nest-asyncio==1.6.0
+ # via livekit-agents
+numpy==2.2.5
+ # via
+ # livekit
+ # livekit-agents
+ # livekit-plugins-deepgram
+ # livekit-plugins-silero
+ # livekit-plugins-turn-detector
+ # onnxruntime
+ # transformers
+oauthlib==3.2.2
+ # via requests-oauthlib
+onnxruntime==1.22.0
+ # via
+ # livekit-plugins-silero
+ # livekit-plugins-turn-detector
+openai==1.78.1
+ # via livekit-plugins-openai
+opentelemetry-api==1.33.0
+ # via
+ # opentelemetry-exporter-otlp-proto-http
+ # opentelemetry-instrumentation
+ # opentelemetry-sdk
+ # opentelemetry-semantic-conventions
+opentelemetry-exporter-otlp-proto-common==1.33.0
+ # via opentelemetry-exporter-otlp-proto-http
+opentelemetry-exporter-otlp-proto-http==1.33.0
+ # via logfire
+opentelemetry-instrumentation==0.54b0
+ # via logfire
+opentelemetry-proto==1.33.0
+ # via
+ # opentelemetry-exporter-otlp-proto-common
+ # opentelemetry-exporter-otlp-proto-http
+opentelemetry-sdk==1.33.0
+ # via
+ # logfire
+ # opentelemetry-exporter-otlp-proto-http
+opentelemetry-semantic-conventions==0.54b0
+ # via
+ # opentelemetry-instrumentation
+ # opentelemetry-sdk
+packaging==25.0
+ # via
+ # huggingface-hub
+ # onnxruntime
+ # opentelemetry-instrumentation
+ # transformers
+pillow==11.2.1
+ # via livekit-agents
+propcache==0.3.1
+ # via
+ # aiohttp
+ # yarl
+proto-plus==1.26.1
+ # via google-api-core
+protobuf==5.29.4
+ # via
+ # google-api-core
+ # googleapis-common-protos
+ # livekit
+ # livekit-agents
+ # livekit-api
+ # livekit-protocol
+ # logfire
+ # onnxruntime
+ # opentelemetry-proto
+ # proto-plus
+psutil==7.0.0
+ # via livekit-agents
+pyasn1==0.6.1
+ # via
+ # pyasn1-modules
+ # rsa
+pyasn1-modules==0.4.2
+ # via google-auth
+pycparser==2.22
+ # via cffi
+pydantic==2.11.4
+ # via
+ # engine (pyproject.toml)
+ # anthropic
+ # fastapi
+ # livekit-agents
+ # mcp
+ # openai
+ # pydantic-settings
+pydantic-core==2.33.2
+ # via pydantic
+pydantic-settings==2.9.1
+ # via
+ # engine (pyproject.toml)
+ # mcp
+pygments==2.19.1
+ # via rich
+pyjwt==2.10.1
+ # via
+ # livekit-agents
+ # livekit-api
+pyparsing==3.2.3
+ # via httplib2
+python-dotenv==1.1.0
+ # via
+ # engine (pyproject.toml)
+ # pydantic-settings
+ # uvicorn
+python-multipart==0.0.20
+ # via mcp
+pyyaml==6.0.2
+ # via
+ # huggingface-hub
+ # transformers
+ # uvicorn
+redis==6.1.0
+ # via spotipy
+regex==2024.11.6
+ # via transformers
+requests==2.32.3
+ # via
+ # google-api-core
+ # huggingface-hub
+ # opentelemetry-exporter-otlp-proto-http
+ # requests-oauthlib
+ # spotipy
+ # transformers
+requests-oauthlib==2.0.0
+ # via google-auth-oauthlib
+rich==14.0.0
+ # via logfire
+rsa==4.9.1
+ # via google-auth
+safetensors==0.5.3
+ # via transformers
+sniffio==1.3.1
+ # via
+ # anthropic
+ # anyio
+ # openai
+sounddevice==0.5.1
+ # via livekit-agents
+spotipy==2.25.1
+ # via engine (pyproject.toml)
+sse-starlette==2.3.5
+ # via mcp
+starlette==0.46.2
+ # via
+ # fastapi
+ # mcp
+ # sse-starlette
+sympy==1.14.0
+ # via onnxruntime
+tokenizers==0.21.1
+ # via transformers
+tqdm==4.67.1
+ # via
+ # huggingface-hub
+ # openai
+ # transformers
+transformers==4.51.3
+ # via livekit-plugins-turn-detector
+types-protobuf==4.25.0.20240417
+ # via
+ # livekit
+ # livekit-agents
+ # livekit-api
+ # livekit-protocol
+typing-extensions==4.13.2
+ # via
+ # engine (pyproject.toml)
+ # anthropic
+ # fastapi
+ # huggingface-hub
+ # livekit-agents
+ # logfire
+ # openai
+ # opentelemetry-sdk
+ # pydantic
+ # pydantic-core
+ # typing-inspection
+typing-inspection==0.4.0
+ # via
+ # pydantic
+ # pydantic-settings
+tzlocal==5.3.1
+ # via apscheduler
+uritemplate==4.1.1
+ # via google-api-python-client
+urllib3==2.4.0
+ # via
+ # requests
+ # spotipy
+uvicorn==0.34.2
+ # via
+ # engine (pyproject.toml)
+ # mcp
+uvloop==0.21.0
+ # via uvicorn
+watchfiles==1.0.5
+ # via
+ # livekit-agents
+ # uvicorn
+websockets==15.0.1
+ # via
+ # openai
+ # uvicorn
+wrapt==1.17.2
+ # via
+ # deprecated
+ # opentelemetry-instrumentation
+yarl==1.20.0
+ # via aiohttp
+zipp==3.21.0
+ # via importlib-metadata
diff --git a/environments/community/router_env/engine/start_stone_agent.sh b/environments/community/router_env/engine/start_stone_agent.sh
new file mode 100755
index 00000000..d7499a2c
--- /dev/null
+++ b/environments/community/router_env/engine/start_stone_agent.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+# Activate the Python virtual environment
+source .router_env/bin/activate
+
+# Install required dependencies if needed
+pip install librosa numpy
+
+# Set the LiveKit server URL and API keys (update these with your actual values)
+export LIVEKIT_URL="pass"
+export LIVEKIT_API_KEY="pass"
+export LIVEKIT_API_SECRET="pass"
+
+# Set OpenAI API key (replace with your actual key)
+export OPENAI_API_KEY="pass"
+
+# Set the room name to match the one in token-server.js
+export LIVEKIT_ROOM="stone-router-voice-agent"
+
+# Start the agent
+cd engine
+python agents/stone_agent.py
diff --git a/environments/community/router_env/engine/tools/mcp/README.md b/environments/community/router_env/engine/tools/mcp/README.md
new file mode 100644
index 00000000..6391321c
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/README.md
@@ -0,0 +1,76 @@
+# MCP Servers Directory
+
+This directory contains all Model Context Protocol (MCP) servers used by the Stone AIOS engine.
+
+## Directory Structure
+
+- `perplexity/`: Perplexity API integration for web search
+ - `perplexity-ask/`: The MCP server for Perplexity's Ask functionality
+- `spotify/`: Spotify API integration for music playback and control
+- Additional MCP servers can be added in their own directories
+
+## Important Notes
+
+1. The code in `engine/agents/` is configured to look for MCP servers in this exact location (`engine/tools/mcp/`).
+
+2. The MCP servers are initially defined as git submodules in `stone_aios/tools/mcp/` but are copied here during setup:
+ - The `start.sh` script copies the servers from their submodule location to this directory.
+ - It then builds the servers in this location to make them available to the engine.
+
+3. When adding new MCP servers:
+ - Add them as submodules in `stone_aios/tools/mcp/`
+ - Update `start.sh` to copy and build them in `engine/tools/mcp/`
+ - Update the agent code to look for them in this location
+
+## Usage
+
+The MCP servers are automatically started when needed by the engine's agent code through the `run_mcp_servers()` context manager in Pydantic-AI.
+
+# Model Context Protocol (MCP) Submodules
+
+This directory contains various Model Context Protocol (MCP) implementations that Stone AIOS uses to interact with different services.
+
+## Submodules
+
+### Perplexity MCP
+- Repository: https://github.com/ppl-ai/modelcontextprotocol.git
+- Purpose: Provides integration with Perplexity's search functionality
+
+### Spotify MCP
+- Repository: https://github.com/varunneal/spotify-mcp.git
+- Purpose: Enables interaction with Spotify's music service
+
+### Basic Memory MCP
+- Repository: https://github.com/basicmachines-co/basic-memory.git
+- Purpose: Provides memory capabilities for agents
+
+### Google Maps MCP
+- Repository: (Google Maps implementation)
+- Purpose: Enables interaction with Google Maps for location-based services
+
+### Google Calendar MCP
+- Repository: https://github.com/nspady/google-calendar-mcp.git
+- Purpose: Provides integration with Google Calendar for managing events and schedules
+
+### Calculator MCP Server
+- Repository: https://github.com/githejie/mcp-server-calculator.git
+- Purpose: Offers calculation capabilities through the MCP protocol
+
+## Usage
+
+These submodules are reference implementations that can be used by Stone AIOS tools. To update all submodules, run:
+
+```bash
+git submodule update --init --recursive
+```
+
+## Adding New MCP Implementations
+
+To add a new MCP implementation:
+
+1. Add it as a git submodule:
+ ```
+ git submodule add tools/mcp/
+ ```
+
+2. Update this README.md file to include information about the new submodule
diff --git a/environments/community/router_env/engine/tools/mcp/calc/calc_server.py b/environments/community/router_env/engine/tools/mcp/calc/calc_server.py
new file mode 100644
index 00000000..e8d3f5ad
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/calc/calc_server.py
@@ -0,0 +1,41 @@
+import logging
+import os
+
+from mcp.server.fastmcp import FastMCP
+
+# Setup logging to a file
+# Adjust the log file path if necessary, perhaps to be relative to this script's location
+# or a dedicated logs directory.
+log_file_path = os.path.join(os.path.dirname(__file__), "math_server_official.log")
+logging.basicConfig(
+ level=logging.DEBUG,
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
+ handlers=[
+ logging.FileHandler(log_file_path, mode="w"), # 'w' to overwrite each run
+ logging.StreamHandler(),
+ ],
+)
+logger = logging.getLogger(__name__)
+
+mcp = FastMCP("Official Math Server 🚀")
+
+
+@mcp.tool()
+def add(a: int, b: int) -> int: # Changed return type hint to int
+ """Add two numbers and return the result"""
+ logger.info(f"Executing add tool with a={a}, b={b}")
+ return a + b
+
+
+@mcp.tool()
+def multiply(a: int, b: int) -> int: # Changed return type hint to int
+ """Multiply two numbers and return the result"""
+ logger.info(f"Executing multiply tool with a={a}, b={b}")
+ return a * b
+
+
+if __name__ == "__main__":
+ logger.info(
+ f"Starting Official MCP math_server.py with STDIO transport... Log file: {log_file_path}"
+ )
+ mcp.run(transport="stdio") # Ensure stdio transport is used as in server_stdio.py
diff --git a/environments/community/router_env/engine/tools/mcp/google-maps/Dockerfile b/environments/community/router_env/engine/tools/mcp/google-maps/Dockerfile
new file mode 100644
index 00000000..7dad5d3f
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/google-maps/Dockerfile
@@ -0,0 +1,25 @@
+FROM node:22.12-alpine AS builder
+
+# Must be entire project because `prepare` script is run during `npm install` and requires all files.
+COPY src/google-maps /app
+COPY tsconfig.json /tsconfig.json
+
+WORKDIR /app
+
+RUN --mount=type=cache,target=/root/.npm npm install
+
+RUN --mount=type=cache,target=/root/.npm-production npm ci --ignore-scripts --omit-dev
+
+FROM node:22-alpine AS release
+
+COPY --from=builder /app/dist /app/dist
+COPY --from=builder /app/package.json /app/package.json
+COPY --from=builder /app/package-lock.json /app/package-lock.json
+
+ENV NODE_ENV=production
+
+WORKDIR /app
+
+RUN npm ci --ignore-scripts --omit-dev
+
+ENTRYPOINT ["node", "dist/index.js"]
diff --git a/environments/community/router_env/engine/tools/mcp/google-maps/MAPS_INTEGRATION.md b/environments/community/router_env/engine/tools/mcp/google-maps/MAPS_INTEGRATION.md
new file mode 100644
index 00000000..c5712ebc
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/google-maps/MAPS_INTEGRATION.md
@@ -0,0 +1,54 @@
+# Google Maps Integration for Stone AIOS
+
+This module enables Stone AIOS to provide location information, directions, and other map-related services using Google Maps.
+
+## Features
+
+- **Location Search**: Find detailed information about places
+- **Directions**: Get directions between locations with different transport modes
+- **Distance Calculation**: Calculate distances and travel times
+- **Place Details**: Get information about businesses, landmarks, etc.
+
+## Requirements
+
+- Google Cloud account with Maps API enabled
+- Google Maps API key with the following APIs enabled:
+ - Maps JavaScript API
+ - Places API
+ - Directions API
+ - Distance Matrix API
+ - Geocoding API
+
+## Configuration
+
+1. Set up a Google Cloud project and enable the necessary Google Maps APIs
+2. Create an API key and restrict it to the Google Maps APIs
+3. Configure your `.env` file with:
+
+```
+GOOGLE_MAPS_API_KEY="your_google_maps_api_key"
+```
+
+## Integration Details
+
+The Google Maps integration uses an MCP server implemented in JavaScript that runs as a subprocess when needed. This ensures the maps service only consumes resources when actively being used.
+
+### Supported Commands
+
+- "Where is the Eiffel Tower?" - Get location information
+- "How do I get from New York to Boston?" - Get directions
+- "How far is it from Los Angeles to San Francisco?" - Calculate distances
+- "What restaurants are near me?" - Find nearby places (requires user location)
+
+## Implementation Notes
+
+The integration is implemented in `agents/stone_agent.py` within the `delegate_to_go_agent` function, which handles:
+
+1. Verifying the presence of a valid Google Maps API key
+2. Starting the Maps MCP server as a subprocess
+3. Processing the query through Claude with map tools access
+4. Returning structured results with location information
+
+## Testing
+
+Tests for the Google Maps integration are available in `tests/ai/test_maps_integration.py`.
diff --git a/environments/community/router_env/engine/tools/mcp/google-maps/README.md b/environments/community/router_env/engine/tools/mcp/google-maps/README.md
new file mode 100644
index 00000000..b91a0657
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/google-maps/README.md
@@ -0,0 +1,114 @@
+# Google Maps MCP Server
+
+MCP Server for the Google Maps API.
+
+## Tools
+
+1. `maps_geocode`
+ - Convert address to coordinates
+ - Input: `address` (string)
+ - Returns: location, formatted_address, place_id
+
+2. `maps_reverse_geocode`
+ - Convert coordinates to address
+ - Inputs:
+ - `latitude` (number)
+ - `longitude` (number)
+ - Returns: formatted_address, place_id, address_components
+
+3. `maps_search_places`
+ - Search for places using text query
+ - Inputs:
+ - `query` (string)
+ - `location` (optional): { latitude: number, longitude: number }
+ - `radius` (optional): number (meters, max 50000)
+ - Returns: array of places with names, addresses, locations
+
+4. `maps_place_details`
+ - Get detailed information about a place
+ - Input: `place_id` (string)
+ - Returns: name, address, contact info, ratings, reviews, opening hours
+
+5. `maps_distance_matrix`
+ - Calculate distances and times between points
+ - Inputs:
+ - `origins` (string[])
+ - `destinations` (string[])
+ - `mode` (optional): "driving" | "walking" | "bicycling" | "transit"
+ - Returns: distances and durations matrix
+
+6. `maps_elevation`
+ - Get elevation data for locations
+ - Input: `locations` (array of {latitude, longitude})
+ - Returns: elevation data for each point
+
+7. `maps_directions`
+ - Get directions between points
+ - Inputs:
+ - `origin` (string)
+ - `destination` (string)
+ - `mode` (optional): "driving" | "walking" | "bicycling" | "transit"
+ - Returns: route details with steps, distance, duration
+
+## Setup
+
+### API Key
+Get a Google Maps API key by following the instructions [here](https://developers.google.com/maps/documentation/javascript/get-api-key#create-api-keys).
+
+### Usage with Claude Desktop
+
+Add the following to your `claude_desktop_config.json`:
+
+#### Docker
+
+```json
+{
+ "mcpServers": {
+ "google-maps": {
+ "command": "docker",
+ "args": [
+ "run",
+ "-i",
+ "--rm",
+ "-e",
+ "GOOGLE_MAPS_API_KEY",
+ "mcp/google-maps"
+ ],
+ "env": {
+ "GOOGLE_MAPS_API_KEY": ""
+ }
+ }
+ }
+}
+```
+
+### NPX
+
+```json
+{
+ "mcpServers": {
+ "google-maps": {
+ "command": "npx",
+ "args": [
+ "-y",
+ "@modelcontextprotocol/server-google-maps"
+ ],
+ "env": {
+ "GOOGLE_MAPS_API_KEY": ""
+ }
+ }
+ }
+}
+```
+
+## Build
+
+Docker build:
+
+```bash
+docker build -t mcp/google-maps -f src/google-maps/Dockerfile .
+```
+
+## License
+
+This MCP server is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
diff --git a/environments/community/router_env/engine/tools/mcp/google-maps/index.ts b/environments/community/router_env/engine/tools/mcp/google-maps/index.ts
new file mode 100644
index 00000000..00bf6eaa
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/google-maps/index.ts
@@ -0,0 +1,678 @@
+#!/usr/bin/env node
+
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import {
+ CallToolRequestSchema,
+ ListToolsRequestSchema,
+ Tool,
+} from "@modelcontextprotocol/sdk/types.js";
+import fetch from "node-fetch";
+
+// Response interfaces
+interface GoogleMapsResponse {
+ status: string;
+ error_message?: string;
+}
+
+interface GeocodeResponse extends GoogleMapsResponse {
+ results: Array<{
+ place_id: string;
+ formatted_address: string;
+ geometry: {
+ location: {
+ lat: number;
+ lng: number;
+ }
+ };
+ address_components: Array<{
+ long_name: string;
+ short_name: string;
+ types: string[];
+ }>;
+ }>;
+}
+
+interface PlacesSearchResponse extends GoogleMapsResponse {
+ results: Array<{
+ name: string;
+ place_id: string;
+ formatted_address: string;
+ geometry: {
+ location: {
+ lat: number;
+ lng: number;
+ }
+ };
+ rating?: number;
+ types: string[];
+ }>;
+}
+
+interface PlaceDetailsResponse extends GoogleMapsResponse {
+ result: {
+ name: string;
+ place_id: string;
+ formatted_address: string;
+ formatted_phone_number?: string;
+ website?: string;
+ rating?: number;
+ reviews?: Array<{
+ author_name: string;
+ rating: number;
+ text: string;
+ time: number;
+ }>;
+ opening_hours?: {
+ weekday_text: string[];
+ open_now: boolean;
+ };
+ geometry: {
+ location: {
+ lat: number;
+ lng: number;
+ }
+ };
+ };
+}
+
+interface DistanceMatrixResponse extends GoogleMapsResponse {
+ origin_addresses: string[];
+ destination_addresses: string[];
+ rows: Array<{
+ elements: Array<{
+ status: string;
+ duration: {
+ text: string;
+ value: number;
+ };
+ distance: {
+ text: string;
+ value: number;
+ };
+ }>;
+ }>;
+}
+
+interface ElevationResponse extends GoogleMapsResponse {
+ results: Array<{
+ elevation: number;
+ location: {
+ lat: number;
+ lng: number;
+ };
+ resolution: number;
+ }>;
+}
+
+interface DirectionsResponse extends GoogleMapsResponse {
+ routes: Array<{
+ summary: string;
+ legs: Array<{
+ distance: {
+ text: string;
+ value: number;
+ };
+ duration: {
+ text: string;
+ value: number;
+ };
+ steps: Array<{
+ html_instructions: string;
+ distance: {
+ text: string;
+ value: number;
+ };
+ duration: {
+ text: string;
+ value: number;
+ };
+ travel_mode: string;
+ }>;
+ }>;
+ }>;
+}
+
+function getApiKey(): string {
+ const apiKey = process.env.GOOGLE_MAPS_API_KEY;
+ if (!apiKey) {
+ console.error("GOOGLE_MAPS_API_KEY environment variable is not set");
+ process.exit(1);
+ }
+ return apiKey;
+ }
+
+const GOOGLE_MAPS_API_KEY = getApiKey();
+
+// Tool definitions
+const GEOCODE_TOOL: Tool = {
+ name: "maps_geocode",
+ description: "Convert an address into geographic coordinates",
+ inputSchema: {
+ type: "object",
+ properties: {
+ address: {
+ type: "string",
+ description: "The address to geocode"
+ }
+ },
+ required: ["address"]
+ }
+ };
+
+const REVERSE_GEOCODE_TOOL: Tool = {
+ name: "maps_reverse_geocode",
+ description: "Convert coordinates into an address",
+ inputSchema: {
+ type: "object",
+ properties: {
+ latitude: {
+ type: "number",
+ description: "Latitude coordinate"
+ },
+ longitude: {
+ type: "number",
+ description: "Longitude coordinate"
+ }
+ },
+ required: ["latitude", "longitude"]
+ }
+};
+
+const SEARCH_PLACES_TOOL: Tool = {
+ name: "maps_search_places",
+ description: "Search for places using Google Places API",
+ inputSchema: {
+ type: "object",
+ properties: {
+ query: {
+ type: "string",
+ description: "Search query"
+ },
+ location: {
+ type: "object",
+ properties: {
+ latitude: { type: "number" },
+ longitude: { type: "number" }
+ },
+ description: "Optional center point for the search"
+ },
+ radius: {
+ type: "number",
+ description: "Search radius in meters (max 50000)"
+ }
+ },
+ required: ["query"]
+ }
+};
+
+const PLACE_DETAILS_TOOL: Tool = {
+ name: "maps_place_details",
+ description: "Get detailed information about a specific place",
+ inputSchema: {
+ type: "object",
+ properties: {
+ place_id: {
+ type: "string",
+ description: "The place ID to get details for"
+ }
+ },
+ required: ["place_id"]
+ }
+};
+
+const DISTANCE_MATRIX_TOOL: Tool = {
+ name: "maps_distance_matrix",
+ description: "Calculate travel distance and time for multiple origins and destinations",
+ inputSchema: {
+ type: "object",
+ properties: {
+ origins: {
+ type: "array",
+ items: { type: "string" },
+ description: "Array of origin addresses or coordinates"
+ },
+ destinations: {
+ type: "array",
+ items: { type: "string" },
+ description: "Array of destination addresses or coordinates"
+ },
+ mode: {
+ type: "string",
+ description: "Travel mode (driving, walking, bicycling, transit)",
+ enum: ["driving", "walking", "bicycling", "transit"]
+ }
+ },
+ required: ["origins", "destinations"]
+ }
+};
+
+const ELEVATION_TOOL: Tool = {
+ name: "maps_elevation",
+ description: "Get elevation data for locations on the earth",
+ inputSchema: {
+ type: "object",
+ properties: {
+ locations: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ latitude: { type: "number" },
+ longitude: { type: "number" }
+ },
+ required: ["latitude", "longitude"]
+ },
+ description: "Array of locations to get elevation for"
+ }
+ },
+ required: ["locations"]
+ }
+};
+
+const DIRECTIONS_TOOL: Tool = {
+ name: "maps_directions",
+ description: "Get directions between two points",
+ inputSchema: {
+ type: "object",
+ properties: {
+ origin: {
+ type: "string",
+ description: "Starting point address or coordinates"
+ },
+ destination: {
+ type: "string",
+ description: "Ending point address or coordinates"
+ },
+ mode: {
+ type: "string",
+ description: "Travel mode (driving, walking, bicycling, transit)",
+ enum: ["driving", "walking", "bicycling", "transit"]
+ }
+ },
+ required: ["origin", "destination"]
+ }
+};
+
+const MAPS_TOOLS = [
+ GEOCODE_TOOL,
+ REVERSE_GEOCODE_TOOL,
+ SEARCH_PLACES_TOOL,
+ PLACE_DETAILS_TOOL,
+ DISTANCE_MATRIX_TOOL,
+ ELEVATION_TOOL,
+ DIRECTIONS_TOOL,
+] as const;
+
+// API handlers
+async function handleGeocode(address: string) {
+ const url = new URL("https://maps.googleapis.com/maps/api/geocode/json");
+ url.searchParams.append("address", address);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as GeocodeResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Geocoding failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ location: data.results[0].geometry.location,
+ formatted_address: data.results[0].formatted_address,
+ place_id: data.results[0].place_id
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+async function handleReverseGeocode(latitude: number, longitude: number) {
+ const url = new URL("https://maps.googleapis.com/maps/api/geocode/json");
+ url.searchParams.append("latlng", `${latitude},${longitude}`);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as GeocodeResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Reverse geocoding failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ formatted_address: data.results[0].formatted_address,
+ place_id: data.results[0].place_id,
+ address_components: data.results[0].address_components
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+async function handlePlaceSearch(
+ query: string,
+ location?: { latitude: number; longitude: number },
+ radius?: number
+) {
+ const url = new URL("https://maps.googleapis.com/maps/api/place/textsearch/json");
+ url.searchParams.append("query", query);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ if (location) {
+ url.searchParams.append("location", `${location.latitude},${location.longitude}`);
+ }
+ if (radius) {
+ url.searchParams.append("radius", radius.toString());
+ }
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as PlacesSearchResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Place search failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ places: data.results.map((place) => ({
+ name: place.name,
+ formatted_address: place.formatted_address,
+ location: place.geometry.location,
+ place_id: place.place_id,
+ rating: place.rating,
+ types: place.types
+ }))
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+async function handlePlaceDetails(place_id: string) {
+ const url = new URL("https://maps.googleapis.com/maps/api/place/details/json");
+ url.searchParams.append("place_id", place_id);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as PlaceDetailsResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Place details request failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ name: data.result.name,
+ formatted_address: data.result.formatted_address,
+ location: data.result.geometry.location,
+ formatted_phone_number: data.result.formatted_phone_number,
+ website: data.result.website,
+ rating: data.result.rating,
+ reviews: data.result.reviews,
+ opening_hours: data.result.opening_hours
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+async function handleDistanceMatrix(
+ origins: string[],
+ destinations: string[],
+ mode: "driving" | "walking" | "bicycling" | "transit" = "driving"
+) {
+ const url = new URL("https://maps.googleapis.com/maps/api/distancematrix/json");
+ url.searchParams.append("origins", origins.join("|"));
+ url.searchParams.append("destinations", destinations.join("|"));
+ url.searchParams.append("mode", mode);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as DistanceMatrixResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Distance matrix request failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ origin_addresses: data.origin_addresses,
+ destination_addresses: data.destination_addresses,
+ results: data.rows.map((row) => ({
+ elements: row.elements.map((element) => ({
+ status: element.status,
+ duration: element.duration,
+ distance: element.distance
+ }))
+ }))
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+async function handleElevation(locations: Array<{ latitude: number; longitude: number }>) {
+ const url = new URL("https://maps.googleapis.com/maps/api/elevation/json");
+ const locationString = locations
+ .map((loc) => `${loc.latitude},${loc.longitude}`)
+ .join("|");
+ url.searchParams.append("locations", locationString);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as ElevationResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Elevation request failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ results: data.results.map((result) => ({
+ elevation: result.elevation,
+ location: result.location,
+ resolution: result.resolution
+ }))
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+async function handleDirections(
+ origin: string,
+ destination: string,
+ mode: "driving" | "walking" | "bicycling" | "transit" = "driving"
+) {
+ const url = new URL("https://maps.googleapis.com/maps/api/directions/json");
+ url.searchParams.append("origin", origin);
+ url.searchParams.append("destination", destination);
+ url.searchParams.append("mode", mode);
+ url.searchParams.append("key", GOOGLE_MAPS_API_KEY);
+
+ const response = await fetch(url.toString());
+ const data = await response.json() as DirectionsResponse;
+
+ if (data.status !== "OK") {
+ return {
+ content: [{
+ type: "text",
+ text: `Directions request failed: ${data.error_message || data.status}`
+ }],
+ isError: true
+ };
+ }
+
+ return {
+ content: [{
+ type: "text",
+ text: JSON.stringify({
+ routes: data.routes.map((route) => ({
+ summary: route.summary,
+ distance: route.legs[0].distance,
+ duration: route.legs[0].duration,
+ steps: route.legs[0].steps.map((step) => ({
+ instructions: step.html_instructions,
+ distance: step.distance,
+ duration: step.duration,
+ travel_mode: step.travel_mode
+ }))
+ }))
+ }, null, 2)
+ }],
+ isError: false
+ };
+}
+
+// Server setup
+const server = new Server(
+ {
+ name: "mcp-server/google-maps",
+ version: "0.1.0",
+ },
+ {
+ capabilities: {
+ tools: {},
+ },
+ },
+);
+
+// Set up request handlers
+server.setRequestHandler(ListToolsRequestSchema, async () => ({
+ tools: MAPS_TOOLS,
+}));
+
+server.setRequestHandler(CallToolRequestSchema, async (request) => {
+ try {
+ switch (request.params.name) {
+ case "maps_geocode": {
+ const { address } = request.params.arguments as { address: string };
+ return await handleGeocode(address);
+ }
+
+ case "maps_reverse_geocode": {
+ const { latitude, longitude } = request.params.arguments as {
+ latitude: number;
+ longitude: number;
+ };
+ return await handleReverseGeocode(latitude, longitude);
+ }
+
+ case "maps_search_places": {
+ const { query, location, radius } = request.params.arguments as {
+ query: string;
+ location?: { latitude: number; longitude: number };
+ radius?: number;
+ };
+ return await handlePlaceSearch(query, location, radius);
+ }
+
+ case "maps_place_details": {
+ const { place_id } = request.params.arguments as { place_id: string };
+ return await handlePlaceDetails(place_id);
+ }
+
+ case "maps_distance_matrix": {
+ const { origins, destinations, mode } = request.params.arguments as {
+ origins: string[];
+ destinations: string[];
+ mode?: "driving" | "walking" | "bicycling" | "transit";
+ };
+ return await handleDistanceMatrix(origins, destinations, mode);
+ }
+
+ case "maps_elevation": {
+ const { locations } = request.params.arguments as {
+ locations: Array<{ latitude: number; longitude: number }>;
+ };
+ return await handleElevation(locations);
+ }
+
+ case "maps_directions": {
+ const { origin, destination, mode } = request.params.arguments as {
+ origin: string;
+ destination: string;
+ mode?: "driving" | "walking" | "bicycling" | "transit";
+ };
+ return await handleDirections(origin, destination, mode);
+ }
+
+ default:
+ return {
+ content: [{
+ type: "text",
+ text: `Unknown tool: ${request.params.name}`
+ }],
+ isError: true
+ };
+ }
+ } catch (error) {
+ return {
+ content: [{
+ type: "text",
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
+ }],
+ isError: true
+ };
+ }
+});
+
+async function runServer() {
+ const transport = new StdioServerTransport();
+ await server.connect(transport);
+ console.error("Google Maps MCP Server running on stdio");
+}
+
+runServer().catch((error) => {
+ console.error("Fatal error running server:", error);
+ process.exit(1);
+});
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/Dockerfile b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/Dockerfile
new file mode 100644
index 00000000..bf44767e
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/Dockerfile
@@ -0,0 +1,21 @@
+FROM node:22.12-alpine AS builder
+
+COPY . /app
+
+WORKDIR /app
+
+RUN --mount=type=cache,target=/root/.npm npm install
+
+FROM node:22-alpine AS release
+
+WORKDIR /app
+
+COPY --from=builder /app/dist /app/dist
+COPY --from=builder /app/package.json /app/package.json
+COPY --from=builder /app/package-lock.json /app/package-lock.json
+
+ENV NODE_ENV=production
+
+RUN npm ci --ignore-scripts --omit-dev
+
+ENTRYPOINT ["node", "dist/index.js"]
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/available_tools.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/available_tools.png
new file mode 100644
index 00000000..e695100b
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/available_tools.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-mcp-directory.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-mcp-directory.png
new file mode 100644
index 00000000..c9b9a28f
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-mcp-directory.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-settings.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-settings.png
new file mode 100644
index 00000000..dfdaefb7
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/cursor-settings.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/demo_screenshot.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/demo_screenshot.png
new file mode 100644
index 00000000..d0b91bac
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/demo_screenshot.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/perplexity-ask-mcp-cursor.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/perplexity-ask-mcp-cursor.png
new file mode 100644
index 00000000..70aadc69
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/perplexity-ask-mcp-cursor.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/system_architecture.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/system_architecture.png
new file mode 100644
index 00000000..3f16b883
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/system_architecture.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/visual-indicator-mcp-tools.png b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/visual-indicator-mcp-tools.png
new file mode 100644
index 00000000..af71d074
Binary files /dev/null and b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/assets/visual-indicator-mcp-tools.png differ
diff --git a/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/index.ts b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/index.ts
new file mode 100644
index 00000000..43f95267
--- /dev/null
+++ b/environments/community/router_env/engine/tools/mcp/perplexity/perplexity-ask/index.ts
@@ -0,0 +1,310 @@
+#!/usr/bin/env node
+
+import { Server } from "@modelcontextprotocol/sdk/server/index.js";
+import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
+import {
+ CallToolRequestSchema,
+ ListToolsRequestSchema,
+ Tool,
+} from "@modelcontextprotocol/sdk/types.js";
+
+/**
+ * Definition of the Perplexity Ask Tool.
+ * This tool accepts an array of messages and returns a chat completion response
+ * from the Perplexity API, with citations appended to the message if provided.
+ */
+const PERPLEXITY_ASK_TOOL: Tool = {
+ name: "perplexity_ask",
+ description:
+ "Engages in a conversation using the Sonar API. " +
+ "Accepts an array of messages (each with a role and content) " +
+ "and returns a ask completion response from the Perplexity model.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ messages: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ role: {
+ type: "string",
+ description: "Role of the message (e.g., system, user, assistant)",
+ },
+ content: {
+ type: "string",
+ description: "The content of the message",
+ },
+ },
+ required: ["role", "content"],
+ },
+ description: "Array of conversation messages",
+ },
+ },
+ required: ["messages"],
+ },
+};
+
+/**
+ * Definition of the Perplexity Research Tool.
+ * This tool performs deep research queries using the Perplexity API.
+ */
+const PERPLEXITY_RESEARCH_TOOL: Tool = {
+ name: "perplexity_research",
+ description:
+ "Performs deep research using the Perplexity API. " +
+ "Accepts an array of messages (each with a role and content) " +
+ "and returns a comprehensive research response with citations.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ messages: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ role: {
+ type: "string",
+ description: "Role of the message (e.g., system, user, assistant)",
+ },
+ content: {
+ type: "string",
+ description: "The content of the message",
+ },
+ },
+ required: ["role", "content"],
+ },
+ description: "Array of conversation messages",
+ },
+ },
+ required: ["messages"],
+ },
+};
+
+/**
+ * Definition of the Perplexity Reason Tool.
+ * This tool performs reasoning queries using the Perplexity API.
+ */
+const PERPLEXITY_REASON_TOOL: Tool = {
+ name: "perplexity_reason",
+ description:
+ "Performs reasoning tasks using the Perplexity API. " +
+ "Accepts an array of messages (each with a role and content) " +
+ "and returns a well-reasoned response using the sonar-reasoning-pro model.",
+ inputSchema: {
+ type: "object",
+ properties: {
+ messages: {
+ type: "array",
+ items: {
+ type: "object",
+ properties: {
+ role: {
+ type: "string",
+ description: "Role of the message (e.g., system, user, assistant)",
+ },
+ content: {
+ type: "string",
+ description: "The content of the message",
+ },
+ },
+ required: ["role", "content"],
+ },
+ description: "Array of conversation messages",
+ },
+ },
+ required: ["messages"],
+ },
+};
+
+// Retrieve the Perplexity API key from environment variables
+const PERPLEXITY_API_KEY = process.env.PERPLEXITY_API_KEY;
+if (!PERPLEXITY_API_KEY) {
+ console.error("Error: PERPLEXITY_API_KEY environment variable is required");
+ process.exit(1);
+}
+
+/**
+ * Performs a chat completion by sending a request to the Perplexity API.
+ * Appends citations to the returned message content if they exist.
+ *
+ * @param {Array<{ role: string; content: string }>} messages - An array of message objects.
+ * @param {string} model - The model to use for the completion.
+ * @returns {Promise} The chat completion result with appended citations.
+ * @throws Will throw an error if the API request fails.
+ */
+async function performChatCompletion(
+ messages: Array<{ role: string; content: string }>,
+ model: string = "sonar-pro"
+): Promise {
+ // Construct the API endpoint URL and request body
+ const url = new URL("https://api.perplexity.ai/chat/completions");
+ const body = {
+ model: model, // Model identifier passed as parameter
+ messages: messages,
+ // Additional parameters can be added here if required (e.g., max_tokens, temperature, etc.)
+ // See the Sonar API documentation for more details:
+ // https://docs.perplexity.ai/api-reference/chat-completions
+ };
+
+ let response;
+ try {
+ response = await fetch(url.toString(), {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${PERPLEXITY_API_KEY}`,
+ },
+ body: JSON.stringify(body),
+ });
+ } catch (error) {
+ throw new Error(`Network error while calling Perplexity API: ${error}`);
+ }
+
+ // Check for non-successful HTTP status
+ if (!response.ok) {
+ let errorText;
+ try {
+ errorText = await response.text();
+ } catch (parseError) {
+ errorText = "Unable to parse error response";
+ }
+ throw new Error(
+ `Perplexity API error: ${response.status} ${response.statusText}\n${errorText}`
+ );
+ }
+
+ // Attempt to parse the JSON response from the API
+ let data;
+ try {
+ data = await response.json();
+ } catch (jsonError) {
+ throw new Error(`Failed to parse JSON response from Perplexity API: ${jsonError}`);
+ }
+
+ // Directly retrieve the main message content from the response
+ let messageContent = data.choices[0].message.content;
+
+ // If citations are provided, append them to the message content
+ if (data.citations && Array.isArray(data.citations) && data.citations.length > 0) {
+ messageContent += "\n\nCitations:\n";
+ data.citations.forEach((citation: string, index: number) => {
+ messageContent += `[${index + 1}] ${citation}\n`;
+ });
+ }
+
+ return messageContent;
+}
+
+// Initialize the server with tool metadata and capabilities
+const server = new Server(
+ {
+ name: "example-servers/perplexity-ask",
+ version: "0.1.0",
+ },
+ {
+ capabilities: {
+ tools: {},
+ },
+ }
+);
+
+/**
+ * Registers a handler for listing available tools.
+ * When the client requests a list of tools, this handler returns all available Perplexity tools.
+ */
+server.setRequestHandler(ListToolsRequestSchema, async () => ({
+ tools: [PERPLEXITY_ASK_TOOL, PERPLEXITY_RESEARCH_TOOL, PERPLEXITY_REASON_TOOL],
+}));
+
+/**
+ * Registers a handler for calling a specific tool.
+ * Processes requests by validating input and invoking the appropriate tool.
+ *
+ * @param {object} request - The incoming tool call request.
+ * @returns {Promise