Nitesh-Reddy commited on
Commit
461b246
Β·
verified Β·
1 Parent(s): 51b5b77

Deploy SecureHeal Agent API

Browse files
Files changed (4) hide show
  1. Dockerfile +12 -0
  2. README.md +5 -8
  3. app.py +193 -0
  4. requirements.txt +7 -0
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY app.py .
9
+
10
+ EXPOSE 7860
11
+
12
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md CHANGED
@@ -1,12 +1,9 @@
1
  ---
2
- title: Secureheal Agent
3
- emoji: 🏒
4
  colorFrom: red
5
  colorTo: purple
6
- sdk: gradio
7
- sdk_version: 6.13.0
8
- app_file: app.py
9
- pinned: false
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: SecureHeal Agent
3
+ emoji: πŸ›‘οΈ
4
  colorFrom: red
5
  colorTo: purple
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: true
 
9
  ---
 
 
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SecureHeal Agent β€” HuggingFace Space FastAPI Server
3
+ ────────────────────────────────────────────────────
4
+ Loads the trained model at startup, caches it, and exposes a FastAPI
5
+ endpoint that takes application code β†’ runs the SecureHeal agent β†’
6
+ finds vulnerabilities β†’ suggests fixes β†’ returns structured response.
7
+
8
+ Deploy to HF Spaces with GPU (T4).
9
+ """
10
+
11
+ import os
12
+ import json
13
+ import re
14
+ import torch
15
+ from contextlib import asynccontextmanager
16
+ from fastapi import FastAPI, HTTPException
17
+ from fastapi.middleware.cors import CORSMiddleware
18
+ from pydantic import BaseModel
19
+ from typing import Optional, List
20
+ from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
21
+
22
+ # ────────────────────── Model Cache ──────────────────────────
23
+
24
+ MODEL_ID = os.environ.get("MODEL_ID", "Nitesh-Reddy/secureheal-agent-v2")
25
+ PIPE = None # Global pipeline β€” loaded once at startup
26
+
27
+
28
+ @asynccontextmanager
29
+ async def lifespan(app: FastAPI):
30
+ """Load model at startup, keep in memory for all requests."""
31
+ global PIPE
32
+ print(f"πŸ”„ Loading model: {MODEL_ID}")
33
+ print(" This takes ~2 min on first load, then cached...")
34
+
35
+ PIPE = pipeline(
36
+ "text-generation",
37
+ model=MODEL_ID,
38
+ torch_dtype=torch.float16,
39
+ device_map="auto",
40
+ )
41
+ print(f"βœ… Model loaded and cached!")
42
+ yield
43
+ print("πŸ›‘ Shutting down...")
44
+
45
+
46
+ # ────────────────────── FastAPI App ──────────────────────────
47
+
48
+ app = FastAPI(
49
+ title="SecureHeal Agent API",
50
+ description="Autonomous SRE & Security agent β€” scans code, finds vulnerabilities, suggests fixes",
51
+ version="1.0.0",
52
+ lifespan=lifespan,
53
+ )
54
+
55
+ app.add_middleware(
56
+ CORSMiddleware,
57
+ allow_origins=["*"],
58
+ allow_methods=["*"],
59
+ allow_headers=["*"],
60
+ )
61
+
62
+
63
+ # ────────────────────── Request/Response Models ──────────────
64
+
65
+ class ScanRequest(BaseModel):
66
+ code: str
67
+ context: Optional[str] = "web application"
68
+ max_tokens: Optional[int] = 512
69
+
70
+ class ToolCall(BaseModel):
71
+ tool: str
72
+ args: dict
73
+
74
+ class VulnerabilityReport(BaseModel):
75
+ vulnerabilities_found: bool
76
+ tool_calls: List[ToolCall]
77
+ analysis: str
78
+ raw_output: str
79
+
80
+ class AgentRequest(BaseModel):
81
+ prompt: str
82
+ max_tokens: Optional[int] = 512
83
+
84
+ class AgentResponse(BaseModel):
85
+ response: str
86
+ tool_calls: List[ToolCall]
87
+
88
+
89
+ # ────────────────────── Helper: Parse Tool Calls ─────────────
90
+
91
+ def parse_tool_calls(text: str) -> List[ToolCall]:
92
+ """Extract <tool_call>tool_name({...})</tool_call> from model output."""
93
+ calls = []
94
+ pattern = r'<tool_call>\s*(\w+)\((\{.*?\})\)\s*</tool_call>'
95
+ matches = re.findall(pattern, text, re.DOTALL)
96
+
97
+ for tool_name, args_str in matches:
98
+ try:
99
+ args = json.loads(args_str)
100
+ except json.JSONDecodeError:
101
+ args = {"raw": args_str}
102
+ calls.append(ToolCall(tool=tool_name, args=args))
103
+
104
+ # Fallback: find tool mentions without proper wrapping
105
+ if not calls:
106
+ valid_tools = [
107
+ "scan_code", "simulate_attack", "apply_patch", "run_tests",
108
+ "restart_service", "clean_data", "reallocate_resources", "classify_issue",
109
+ ]
110
+ for tool in valid_tools:
111
+ if tool in text.lower():
112
+ calls.append(ToolCall(tool=tool, args={}))
113
+
114
+ return calls
115
+
116
+
117
+ # ────────────────────── Endpoints ────────────────────────────
118
+
119
+ @app.get("/")
120
+ async def root():
121
+ return {
122
+ "service": "SecureHeal Agent",
123
+ "model": MODEL_ID,
124
+ "status": "ready" if PIPE else "loading",
125
+ "endpoints": {
126
+ "/scan": "POST β€” Scan code for vulnerabilities",
127
+ "/agent": "POST β€” Free-form agent prompt",
128
+ "/health": "GET β€” Health check",
129
+ },
130
+ }
131
+
132
+
133
+ @app.get("/health")
134
+ async def health():
135
+ return {"status": "healthy", "model_loaded": PIPE is not None}
136
+
137
+
138
+ @app.post("/scan", response_model=VulnerabilityReport)
139
+ async def scan_code(request: ScanRequest):
140
+ """
141
+ Scan application code for vulnerabilities.
142
+ The agent analyzes the code and returns structured tool calls + fixes.
143
+ """
144
+ if not PIPE:
145
+ raise HTTPException(503, "Model still loading, try again in ~2 min")
146
+
147
+ prompt = (
148
+ f"You are an autonomous SRE and Security agent. "
149
+ f"Analyze the following {request.context} code for vulnerabilities. "
150
+ f"Use scan_code, simulate_attack, apply_patch, run_tests to analyze and fix. "
151
+ f"Output each action as <tool_call>tool_name({{\"param\": \"value\"}})</tool_call>. "
152
+ f"End with DONE when finished.\n\n"
153
+ f"Code to analyze:\n```\n{request.code}\n```"
154
+ )
155
+
156
+ messages = [{"role": "user", "content": prompt}]
157
+ output = PIPE(messages, max_new_tokens=request.max_tokens, do_sample=True, temperature=0.7)
158
+ response_text = output[0]["generated_text"][-1]["content"]
159
+
160
+ tool_calls = parse_tool_calls(response_text)
161
+
162
+ return VulnerabilityReport(
163
+ vulnerabilities_found=len(tool_calls) > 0,
164
+ tool_calls=tool_calls,
165
+ analysis=response_text,
166
+ raw_output=response_text,
167
+ )
168
+
169
+
170
+ @app.post("/agent", response_model=AgentResponse)
171
+ async def agent_prompt(request: AgentRequest):
172
+ """
173
+ Send a free-form prompt to the SecureHeal agent.
174
+ """
175
+ if not PIPE:
176
+ raise HTTPException(503, "Model still loading, try again in ~2 min")
177
+
178
+ messages = [{"role": "user", "content": request.prompt}]
179
+ output = PIPE(messages, max_new_tokens=request.max_tokens, do_sample=True, temperature=0.7)
180
+ response_text = output[0]["generated_text"][-1]["content"]
181
+ tool_calls = parse_tool_calls(response_text)
182
+
183
+ return AgentResponse(
184
+ response=response_text,
185
+ tool_calls=tool_calls,
186
+ )
187
+
188
+
189
+ # ────────────────────── Run ──────────────────────────────────
190
+
191
+ if __name__ == "__main__":
192
+ import uvicorn
193
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ transformers>=4.46
2
+ torch>=2.0
3
+ accelerate
4
+ bitsandbytes
5
+ fastapi
6
+ uvicorn[standard]
7
+ pydantic>=2.0