Commit ·
cd688d7
0
Parent(s):
Initial Commit
Browse files- .env.example +3 -0
- .github/workflows/openenv-validation.yml +37 -0
- .gitignore +18 -0
- Dockerfile +18 -0
- PRD.md +25 -0
- README.md +41 -0
- inference.py +127 -0
- openenv.yaml +29 -0
- pyproject.toml +21 -0
- requirements.txt +5 -0
- server/app.py +53 -0
.env.example
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
API_BASE_URL="https://generativelanguage.googleapis.com/v1beta/openai/"
|
| 2 |
+
MODEL_NAME="gemini-2.5-flash"
|
| 3 |
+
OPENAI_API_KEY="YOUR_KEY_HERE"
|
.github/workflows/openenv-validation.yml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: OpenEnv Validation CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ "main", "master" ]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [ "main", "master" ]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
validate:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
|
| 13 |
+
steps:
|
| 14 |
+
- name: Checkout Repository
|
| 15 |
+
uses: actions/checkout@v4
|
| 16 |
+
|
| 17 |
+
- name: Set up Python
|
| 18 |
+
uses: actions/setup-python@v4
|
| 19 |
+
with:
|
| 20 |
+
python-version: '3.11'
|
| 21 |
+
|
| 22 |
+
- name: Install dependencies and uv
|
| 23 |
+
run: |
|
| 24 |
+
python -m pip install --upgrade pip
|
| 25 |
+
pip install uv
|
| 26 |
+
pip install openenv-core>=0.2.0
|
| 27 |
+
|
| 28 |
+
- name: Lock dependencies
|
| 29 |
+
run: uv lock
|
| 30 |
+
|
| 31 |
+
- name: Run OpenEnv Validate
|
| 32 |
+
run: |
|
| 33 |
+
openenv validate .
|
| 34 |
+
|
| 35 |
+
- name: Verify Docker Builds
|
| 36 |
+
run: |
|
| 37 |
+
docker build -t test-openenv .
|
.gitignore
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Virtual Environments
|
| 2 |
+
.venv/
|
| 3 |
+
venv/
|
| 4 |
+
env/
|
| 5 |
+
|
| 6 |
+
# Python caching
|
| 7 |
+
__pycache__/
|
| 8 |
+
*.pyc
|
| 9 |
+
.pytest_cache/
|
| 10 |
+
|
| 11 |
+
# Environment Variables
|
| 12 |
+
.env
|
| 13 |
+
|
| 14 |
+
# MacOS
|
| 15 |
+
.DS_Store
|
| 16 |
+
|
| 17 |
+
# Tool outputs (uv)
|
| 18 |
+
uv.lock
|
Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.11-slim
|
| 2 |
+
|
| 3 |
+
WORKDIR /app
|
| 4 |
+
|
| 5 |
+
# Install dependencies directly to be lightweight
|
| 6 |
+
RUN pip install --no-cache-dir pydantic openai fastapi uvicorn
|
| 7 |
+
|
| 8 |
+
# Copy project files
|
| 9 |
+
COPY . .
|
| 10 |
+
|
| 11 |
+
# Set default env vars
|
| 12 |
+
ENV PYTHONUNBUFFERED=1
|
| 13 |
+
|
| 14 |
+
# Expose HF Spaces port
|
| 15 |
+
EXPOSE 7860
|
| 16 |
+
|
| 17 |
+
# Run the FastAPI server by default
|
| 18 |
+
CMD ["uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "7860"]
|
PRD.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Product Requirements Document (PRD): Support Ticket Environment for OpenEnv
|
| 2 |
+
|
| 3 |
+
## 1. Introduction and Objectives
|
| 4 |
+
The **Support Ticket Environment** aims to test Large Language Models (LLMs) and agentic frameworks in a highly realistic, consequence-driven enterprise setting. Customer support resolution requires strict adherence to internal policies, information verification, and multi-step reasoning before taking terminal actions (e.g., refunds or escalations).
|
| 5 |
+
|
| 6 |
+
**Objective**: Provide an OpenEnv-compliant simulation where an agent assumes the role of a support professional. The environment acts as an adversarial and deterministic evaluator to cleanly quantify an agent's ability to gather state, read contextual rules, and execute appropriate API actions.
|
| 7 |
+
|
| 8 |
+
## 2. Real-World Utility
|
| 9 |
+
Most AI evaluations focus on static benchmarks (MMLU) or gamified environments (Minecraft). However, the most immediate commercial application of agentic AI is customer support automation.
|
| 10 |
+
* **The Problem**: Companies lose millions to unchecked LLM agents hallucinating policies, issuing improper refunds, or frustrating high-tier enterprise clients.
|
| 11 |
+
* **The Solution**: This environment models the actual complexity of a ticketing system. It enforces that agents must securely verify `UserData`, correctly attribute `IssueType` to a `Policy`, and avoid taking destructive actions (like rejecting an enterprise client abruptly) under pressure or when faced with confusing queries.
|
| 12 |
+
|
| 13 |
+
## 3. Environment Architecture
|
| 14 |
+
- **State Boundaries**: Each task begins with a newly opened ticket. The episode terminates either when the agent explicitly uses a terminal action (`close_ticket`, `escalate`) or after reaching the hard threshold of $N=10$ steps.
|
| 15 |
+
- **Action Constraints**: Intermediate actions (`fetch_user_data`, `check_policy`) do not alter the external ticket state but provide critical context. Terminal actions irreversibly mutate the state and trigger evaluation.
|
| 16 |
+
- **Grading and Reward Shaping**:
|
| 17 |
+
- Graders are strictly deterministic.
|
| 18 |
+
- Fractional rewards are yielded for necessary intermediate contextualization steps (promoting chain-of-thought grounding).
|
| 19 |
+
- Sharp penalties are applied for protocol violations (e.g., escalating a simple refund directly to billing Tier 2).
|
| 20 |
+
|
| 21 |
+
## 4. Required Agent Capabilities
|
| 22 |
+
To succeed on hard tasks, an agent must demonstrate:
|
| 23 |
+
- **State Management**: Remembering the constraints of the `policy` retrieved earlier in the episode.
|
| 24 |
+
- **Self-Correction**: Adapting if `fetch_user_data` returns constraints (e.g., the user is not a premium member).
|
| 25 |
+
- **Nuanced Execution**: Apologizing organically when generating the `reply_to_customer` response during a high-stakes failure ticket.
|
README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# OpenEnv: Support Ticket Resolution System
|
| 2 |
+
|
| 3 |
+
An OpenEnv standards-compliant simulated customer support environment. The agent takes the role of a support professional and resolves tickets using realistic multi-step processes such as verifying users, checking policies, and issuing actions (refunds, escalations, replies).
|
| 4 |
+
|
| 5 |
+
## Motivation & Real-world Relevance
|
| 6 |
+
*Please see our detailed [Product Requirements Document (PRD.md)](./PRD.md) for full breakdown.*
|
| 7 |
+
|
| 8 |
+
Most AI evaluations involve games or static code benchmarks. This environment measures how accurately an agent can navigate a realistic business process, following internal company logic before issuing potentially destructive operations (e.g., refunds or enterprise escalations). It rewards adherence to protocol (partial rewards for checking policy) and penalizes hasty or contradictory actions.
|
| 9 |
+
|
| 10 |
+
## Tasks
|
| 11 |
+
* **Easy (`task_easy_1`)**: Straightforward accidental purchase refund. Agent simply checks policy, refunds, and closes.
|
| 12 |
+
* **Medium (`task_medium_1`)**: Refund request clearly violating policy. Agent must politely reject and close, not refund.
|
| 13 |
+
* **Hard (`task_hard_1`)**: Enterprise customer complains about multi-month double charges. Agent must verify user data, realize the urgency of tier 2 support, apologize, and properly escalate without closing abruptly.
|
| 14 |
+
|
| 15 |
+
## Action Space
|
| 16 |
+
`fetch_user_data(user_id)`
|
| 17 |
+
`check_policy(issue_type)`
|
| 18 |
+
`issue_refund(amount)`
|
| 19 |
+
`reply_to_customer(message)`
|
| 20 |
+
`escalate(reason)`
|
| 21 |
+
`close_ticket(resolution)`
|
| 22 |
+
|
| 23 |
+
## Observation Space
|
| 24 |
+
Provides details on the current `ticket`, `available_actions`, `history` of past actions, active `system_message`, and the latest `tool_output`.
|
| 25 |
+
|
| 26 |
+
## Setup and Run
|
| 27 |
+
|
| 28 |
+
Using Docker:
|
| 29 |
+
```bash
|
| 30 |
+
docker build -t openenv_support .
|
| 31 |
+
# Run API Server (HF Spaces mode):
|
| 32 |
+
docker run -p 7860:7860 openenv_support
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
Run baseline inference test script locally:
|
| 36 |
+
Ensure you install `pydantic` and `openai` first.
|
| 37 |
+
```bash
|
| 38 |
+
export OPENAI_API_KEY="your-key"
|
| 39 |
+
export MODEL_NAME="gpt-4o"
|
| 40 |
+
python inference.py
|
| 41 |
+
```
|
inference.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import json
|
| 3 |
+
import asyncio
|
| 4 |
+
from typing import List, Optional
|
| 5 |
+
from openai import OpenAI
|
| 6 |
+
from env.environment import SupportTicketEnv
|
| 7 |
+
from env.models import Action
|
| 8 |
+
|
| 9 |
+
API_BASE_URL = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
|
| 10 |
+
MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o-mini")
|
| 11 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 12 |
+
LOCAL_IMAGE_NAME = os.getenv("LOCAL_IMAGE_NAME")
|
| 13 |
+
|
| 14 |
+
MAX_STEPS = 10
|
| 15 |
+
MAX_TOTAL_REWARD = 1.0
|
| 16 |
+
SUCCESS_SCORE_THRESHOLD = 0.8
|
| 17 |
+
|
| 18 |
+
def log_start(task: str, env: str, model: str):
|
| 19 |
+
print(f"[START] task={task} env={env} model={model}", flush=True)
|
| 20 |
+
|
| 21 |
+
def log_step(step: int, action: str, reward: float, done: bool, error: Optional[str] = None):
|
| 22 |
+
err_str = f" error={error}" if error else ""
|
| 23 |
+
print(f"[STEP] step={step} action={action!r} reward={reward} done={done}{err_str}", flush=True)
|
| 24 |
+
|
| 25 |
+
def log_end(success: bool, steps: int, score: float, rewards: list):
|
| 26 |
+
print(f"[END] success={success} steps={steps} score={score} rewards={rewards}", flush=True)
|
| 27 |
+
|
| 28 |
+
def parse_action(text: str) -> Action:
|
| 29 |
+
try:
|
| 30 |
+
start_idx = text.find('{')
|
| 31 |
+
end_idx = text.rfind('}') + 1
|
| 32 |
+
if start_idx != -1 and end_idx != -1:
|
| 33 |
+
json_str = text[start_idx:end_idx]
|
| 34 |
+
data = json.loads(json_str)
|
| 35 |
+
return Action(
|
| 36 |
+
action_type=data.get("action_type", "close_ticket"),
|
| 37 |
+
parameters=data.get("parameters", {})
|
| 38 |
+
)
|
| 39 |
+
except Exception:
|
| 40 |
+
pass
|
| 41 |
+
return Action(action_type="close_ticket", parameters={"resolution": "invalid"})
|
| 42 |
+
|
| 43 |
+
def get_model_message(client, step: int, env_state: str, history: List[str]) -> str:
|
| 44 |
+
system_prompt = (
|
| 45 |
+
"You are an AI support agent resolving customer tickets.\n"
|
| 46 |
+
"Available Actions:\n"
|
| 47 |
+
"- fetch_user_data(user_id)\n"
|
| 48 |
+
"- check_policy(issue_type)\n"
|
| 49 |
+
"- issue_refund(amount)\n"
|
| 50 |
+
"- reply_to_customer(message)\n"
|
| 51 |
+
"- escalate(reason)\n"
|
| 52 |
+
"- close_ticket(resolution)\n\n"
|
| 53 |
+
"Must respond with JSON format:\n"
|
| 54 |
+
"{\"action_type\": \"...\", \"parameters\": {\"...\": \"...\"}}"
|
| 55 |
+
)
|
| 56 |
+
history_str = "\n".join(history)
|
| 57 |
+
user_prompt = f"History:\n{history_str}\n\nCurrent Observation:\n{env_state}\n\nWhat is your next action JSON?"
|
| 58 |
+
|
| 59 |
+
try:
|
| 60 |
+
completion = client.chat.completions.create(
|
| 61 |
+
model=MODEL_NAME,
|
| 62 |
+
messages=[
|
| 63 |
+
{"role": "system", "content": system_prompt},
|
| 64 |
+
{"role": "user", "content": user_prompt}
|
| 65 |
+
],
|
| 66 |
+
temperature=0.1
|
| 67 |
+
)
|
| 68 |
+
text = (completion.choices[0].message.content or "").strip()
|
| 69 |
+
return text if text else "{}"
|
| 70 |
+
except Exception as exc:
|
| 71 |
+
print(f"[DEBUG] Model request failed: {exc}", flush=True)
|
| 72 |
+
return "{}"
|
| 73 |
+
|
| 74 |
+
async def run_task(task_id: str, client: OpenAI) -> None:
|
| 75 |
+
env = SupportTicketEnv(task_id=task_id)
|
| 76 |
+
history: List[str] = []
|
| 77 |
+
rewards: List[float] = []
|
| 78 |
+
steps_taken = 0
|
| 79 |
+
score = 0.0
|
| 80 |
+
success = False
|
| 81 |
+
|
| 82 |
+
log_start(task=task_id, env="SupportTicketEnv", model=MODEL_NAME)
|
| 83 |
+
try:
|
| 84 |
+
obs = env.reset()
|
| 85 |
+
last_echoed = obs.model_dump_json(indent=2)
|
| 86 |
+
last_reward = 0.0
|
| 87 |
+
|
| 88 |
+
for step in range(1, MAX_STEPS + 1):
|
| 89 |
+
if env.state.is_done:
|
| 90 |
+
break
|
| 91 |
+
|
| 92 |
+
message = get_model_message(client, step, last_echoed, history)
|
| 93 |
+
action = parse_action(message)
|
| 94 |
+
|
| 95 |
+
obs_obj, reward, done, info = env.step(action)
|
| 96 |
+
obs_json = obs_obj.model_dump_json(indent=2)
|
| 97 |
+
error = None
|
| 98 |
+
|
| 99 |
+
actual_reward = info.get("current_reward", 0.0)
|
| 100 |
+
|
| 101 |
+
rewards.append(actual_reward)
|
| 102 |
+
steps_taken = step
|
| 103 |
+
last_echoed = obs_json
|
| 104 |
+
last_reward = actual_reward
|
| 105 |
+
|
| 106 |
+
log_step(step=step, action=message, reward=actual_reward, done=done, error=error)
|
| 107 |
+
history.append(f"Step {step}: {message!r} -> reward {actual_reward:+.2f}")
|
| 108 |
+
|
| 109 |
+
if done:
|
| 110 |
+
score = actual_reward
|
| 111 |
+
break
|
| 112 |
+
|
| 113 |
+
score = min(max(score, 0.0), 1.0)
|
| 114 |
+
success = score >= SUCCESS_SCORE_THRESHOLD
|
| 115 |
+
finally:
|
| 116 |
+
log_end(success=success, steps=steps_taken, score=score, rewards=rewards)
|
| 117 |
+
|
| 118 |
+
async def main() -> None:
|
| 119 |
+
api_key = os.getenv("OPENAI_API_KEY", "dummy-key")
|
| 120 |
+
client = OpenAI(base_url=API_BASE_URL, api_key=api_key)
|
| 121 |
+
|
| 122 |
+
tasks = ["task_easy_1", "task_medium_1", "task_hard_1"]
|
| 123 |
+
for task_id in tasks:
|
| 124 |
+
await run_task(task_id, client)
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
asyncio.run(main())
|
openenv.yaml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "SupportTicketEnv"
|
| 2 |
+
version: "1.0.0"
|
| 3 |
+
description: >
|
| 4 |
+
A real-world OpenEnv environment simulating a customer support ticketing system.
|
| 5 |
+
The agent must process open tickets by optionally fetching user data, checking internal policies,
|
| 6 |
+
and taking terminal actions like issuing a refund, replying, escalating, or closing the ticket.
|
| 7 |
+
action_space:
|
| 8 |
+
type: "dict"
|
| 9 |
+
schema: |
|
| 10 |
+
{
|
| 11 |
+
"action_type": "[fetch_user_data, check_policy, issue_refund, reply_to_customer, escalate, close_ticket]",
|
| 12 |
+
"parameters": {"param_name": "param_value"}
|
| 13 |
+
}
|
| 14 |
+
observation_space:
|
| 15 |
+
type: "dict"
|
| 16 |
+
schema: |
|
| 17 |
+
{
|
| 18 |
+
"ticket": {"TicketInfo object"},
|
| 19 |
+
"available_actions": ["list of strings"],
|
| 20 |
+
"system_message": "string",
|
| 21 |
+
"history": ["List of strings of past actions"],
|
| 22 |
+
"tool_output": "Optional string of the latest action output",
|
| 23 |
+
"step_count": "integer"
|
| 24 |
+
}
|
| 25 |
+
reward_description: >
|
| 26 |
+
The reward is between 0.0 and 1.0. Partial credit is given for taking correct
|
| 27 |
+
intermediate steps (like checking policy before acting or fetching user data).
|
| 28 |
+
Penalties are applied for taking contradictory or destructive actions
|
| 29 |
+
(e.g., escalating unnecessarily, issuing refunds against policy).
|
pyproject.toml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["hatchling"]
|
| 3 |
+
build-backend = "hatchling.build"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "support-ticket-env"
|
| 7 |
+
version = "1.0.0"
|
| 8 |
+
description = "A real-world OpenEnv environment simulating a customer support ticketing system."
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
dependencies = [
|
| 11 |
+
"pydantic>=2.0",
|
| 12 |
+
"openenv-core>=0.2.0",
|
| 13 |
+
]
|
| 14 |
+
|
| 15 |
+
[project.scripts]
|
| 16 |
+
server = "server.app:main"
|
| 17 |
+
|
| 18 |
+
[project.optional-dependencies]
|
| 19 |
+
dev = [
|
| 20 |
+
"pytest",
|
| 21 |
+
]
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pydantic>=2.0
|
| 2 |
+
openai>=1.0.0
|
| 3 |
+
fastapi
|
| 4 |
+
uvicorn
|
| 5 |
+
openenv-core
|
server/app.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, HTTPException
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
from env.environment import SupportTicketEnv
|
| 4 |
+
from env.models import Action
|
| 5 |
+
|
| 6 |
+
app = FastAPI(title="OpenEnv Support Ticket API")
|
| 7 |
+
|
| 8 |
+
CURRENT_ENV_SESSION = None
|
| 9 |
+
|
| 10 |
+
class InitRequest(BaseModel):
|
| 11 |
+
task_id: str = "task_easy_1"
|
| 12 |
+
|
| 13 |
+
@app.get("/")
|
| 14 |
+
def read_root():
|
| 15 |
+
return {"status": "ok", "message": "Support Ticket OpenEnv is live."}
|
| 16 |
+
|
| 17 |
+
@app.post("/reset")
|
| 18 |
+
def reset_env(req: InitRequest):
|
| 19 |
+
global CURRENT_ENV_SESSION
|
| 20 |
+
try:
|
| 21 |
+
CURRENT_ENV_SESSION = SupportTicketEnv(task_id=req.task_id)
|
| 22 |
+
obs = CURRENT_ENV_SESSION.reset()
|
| 23 |
+
return {"observation": obs.model_dump()}
|
| 24 |
+
except ValueError as e:
|
| 25 |
+
raise HTTPException(status_code=400, detail=str(e))
|
| 26 |
+
|
| 27 |
+
@app.post("/step")
|
| 28 |
+
def step_env(action: Action):
|
| 29 |
+
global CURRENT_ENV_SESSION
|
| 30 |
+
if not CURRENT_ENV_SESSION:
|
| 31 |
+
raise HTTPException(status_code=400, detail="Environment not initialized. Call /reset first.")
|
| 32 |
+
|
| 33 |
+
obs, reward, done, info = CURRENT_ENV_SESSION.step(action)
|
| 34 |
+
return {
|
| 35 |
+
"observation": obs.model_dump(),
|
| 36 |
+
"reward": reward,
|
| 37 |
+
"done": done,
|
| 38 |
+
"info": info
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
@app.get("/state")
|
| 42 |
+
def state_env():
|
| 43 |
+
global CURRENT_ENV_SESSION
|
| 44 |
+
if not CURRENT_ENV_SESSION:
|
| 45 |
+
raise HTTPException(status_code=400, detail="Environment not initialized.")
|
| 46 |
+
return CURRENT_ENV_SESSION.get_state().model_dump()
|
| 47 |
+
|
| 48 |
+
def main():
|
| 49 |
+
import uvicorn
|
| 50 |
+
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 51 |
+
|
| 52 |
+
if __name__ == "__main__":
|
| 53 |
+
main()
|