promptreview-proxy 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: promptreview-proxy
3
+ Version: 0.1.0
4
+ Summary: Local HTTP proxy forwarding engine for PromptReview VS Code extension with API subdomain analytics.
5
+ Author-email: PromptReview Team <support@prompt-guard-ai.com>
6
+ License: MIT
7
+ Keywords: llm,proxy,fastapi,vscode,promptreview,ai
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Framework :: FastAPI
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: fastapi>=0.110.0
22
+ Requires-Dist: uvicorn>=0.28.0
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: pydantic>=2.6.0
25
+
26
+ # PromptReview Proxy (`promptreview-proxy`)
27
+
28
+ `promptreview-proxy` is a lightweight, local HTTP proxy forwarding server for the PromptReview VS Code Extension.
29
+
30
+ ## Features
31
+ - **Local HTTP Proxying**: Intercepts outbound LLM traffic from VS Code (`http.proxy`).
32
+ - **Cloud Analytics Relay**: Asynchronously logs prompt metadata to your API subdomain (`https://api.prompt-guard-ai.com/v1/prompts`).
33
+ - **Dynamic Provider Routing**: Forwards requests to target LLM providers (OpenAI, Anthropic, Groq, Ollama, etc.) and streams responses back to VS Code.
34
+
35
+ ## Quick Start
36
+
37
+ ### Installation
38
+ ```bash
39
+ pip install promptreview-proxy
40
+ ```
41
+
42
+ ### Usage
43
+ Run the proxy server from command line:
44
+ ```bash
45
+ promptreview-proxy
46
+ ```
47
+
48
+ Or configure environment variables:
49
+ ```bash
50
+ export PROXY_PORT=6767
51
+ export API_BASE_URL="https://api.prompt-guard-ai.com"
52
+ promptreview-proxy
53
+ ```
@@ -0,0 +1,28 @@
1
+ # PromptReview Proxy (`promptreview-proxy`)
2
+
3
+ `promptreview-proxy` is a lightweight, local HTTP proxy forwarding server for the PromptReview VS Code Extension.
4
+
5
+ ## Features
6
+ - **Local HTTP Proxying**: Intercepts outbound LLM traffic from VS Code (`http.proxy`).
7
+ - **Cloud Analytics Relay**: Asynchronously logs prompt metadata to your API subdomain (`https://api.prompt-guard-ai.com/v1/prompts`).
8
+ - **Dynamic Provider Routing**: Forwards requests to target LLM providers (OpenAI, Anthropic, Groq, Ollama, etc.) and streams responses back to VS Code.
9
+
10
+ ## Quick Start
11
+
12
+ ### Installation
13
+ ```bash
14
+ pip install promptreview-proxy
15
+ ```
16
+
17
+ ### Usage
18
+ Run the proxy server from command line:
19
+ ```bash
20
+ promptreview-proxy
21
+ ```
22
+
23
+ Or configure environment variables:
24
+ ```bash
25
+ export PROXY_PORT=6767
26
+ export API_BASE_URL="https://api.prompt-guard-ai.com"
27
+ promptreview-proxy
28
+ ```
@@ -0,0 +1,3 @@
1
+ """PromptReview Proxy Package"""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,198 @@
1
+ import asyncio
2
+ import json
3
+ import os
4
+ import sys
5
+ from typing import Any, AsyncGenerator
6
+
7
+ import httpx
8
+ from fastapi import FastAPI, Header, HTTPException, Request, Response
9
+ from fastapi.responses import JSONResponse, StreamingResponse
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class ProxyConfig:
14
+ def __init__(self) -> None:
15
+ self.host = os.getenv("PROXY_HOST", "127.0.0.1")
16
+ self.port = int(os.getenv("PROXY_PORT", "6767"))
17
+
18
+ self.api_base_url = os.getenv("API_BASE_URL", "https://api.prompt-guard-ai.com").rstrip("/")
19
+ self.api_token = os.getenv("API_TOKEN", "")
20
+
21
+ self.llm_provider_url = os.getenv("LLM_PROVIDER_URL", "https://api.openai.com/v1/chat/completions")
22
+ self.llm_provider_api_key = os.getenv("LLM_PROVIDER_API_KEY", "")
23
+
24
+
25
+ config = ProxyConfig()
26
+ app = FastAPI(title="PromptReview Local Proxy", version="0.1.0")
27
+
28
+
29
+ class ProxyChatRequest(BaseModel):
30
+ company: str = Field(..., description="Organization name or email (C1)")
31
+ project: str = Field(default="default-project", description="Project name (P1)")
32
+ user: str = Field(..., description="User identity or email (U1)")
33
+ prompt: str = Field(..., min_length=1, description="Prompt string")
34
+ model: str | None = Field(default="gpt-4o", description="Target LLM model")
35
+ messages: list[dict[str, Any]] | None = Field(default=None, description="Optional messages payload")
36
+ metadata: dict[str, Any] | None = Field(default_factory=dict)
37
+
38
+
39
+ async def send_event_to_api_subdomain(payload: dict[str, Any]) -> None:
40
+ """Asynchronously dispatch prompt event to API subdomain for cloud storage."""
41
+ target_url = f"{config.api_base_url}/v1/prompts"
42
+ headers = {"Content-Type": "application/json"}
43
+ if config.api_token:
44
+ headers["Authorization"] = f"Bearer {config.api_token}"
45
+
46
+ try:
47
+ async with httpx.AsyncClient(timeout=10.0) as client:
48
+ res = await client.post(target_url, json=payload, headers=headers)
49
+ if res.status_code >= 400:
50
+ print(f"[promptreview-proxy] API subdomain warning ({res.status_code}): {res.text}", file=sys.stderr)
51
+ except Exception as exc:
52
+ print(f"[promptreview-proxy] Failed sending event to API subdomain: {exc}", file=sys.stderr)
53
+
54
+
55
+ @app.get("/health")
56
+ async def health() -> dict[str, str]:
57
+ return {"status": "ok", "service": "promptreview-proxy"}
58
+
59
+
60
+ @app.post("/v1/proxy/chat")
61
+ async def proxy_chat(
62
+ request: ProxyChatRequest,
63
+ x_company: str | None = Header(default=None),
64
+ x_project: str | None = Header(default=None),
65
+ x_user: str | None = Header(default=None),
66
+ authorization: str | None = Header(default=None),
67
+ ) -> dict[str, Any]:
68
+ company = request.company or x_company or "UnknownCompany"
69
+ project = request.project or x_project or "default-project"
70
+ user = request.user or x_user or "UnknownUser"
71
+
72
+ api_payload = {
73
+ "company": company,
74
+ "project": project,
75
+ "user": user,
76
+ "prompt": request.prompt,
77
+ "model": request.model,
78
+ "messages": request.messages or [{"role": "user", "content": request.prompt}],
79
+ "metadata": request.metadata or {},
80
+ }
81
+
82
+ asyncio.create_task(send_event_to_api_subdomain(api_payload))
83
+
84
+ llm_payload = {
85
+ "model": request.model,
86
+ "messages": request.messages or [{"role": "user", "content": request.prompt}],
87
+ }
88
+
89
+ headers = {"Content-Type": "application/json"}
90
+ if authorization:
91
+ headers["Authorization"] = authorization
92
+ elif config.llm_provider_api_key:
93
+ headers["Authorization"] = f"Bearer {config.llm_provider_api_key}"
94
+
95
+ try:
96
+ async with httpx.AsyncClient(timeout=60.0) as client:
97
+ llm_res = await client.post(config.llm_provider_url, json=llm_payload, headers=headers)
98
+ llm_res.raise_for_status()
99
+ return {
100
+ "ok": True,
101
+ "company": company,
102
+ "project": project,
103
+ "user": user,
104
+ "provider_response": llm_res.json(),
105
+ }
106
+ except Exception as exc:
107
+ raise HTTPException(status_code=502, detail=f"LLM Provider error: {str(exc)}")
108
+
109
+
110
+ @app.api_route("/v1/chat/completions", methods=["POST", "OPTIONS"])
111
+ @app.api_route("/{full_path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"])
112
+ async def catch_all_proxy(request: Request, full_path: str = "") -> Response:
113
+ if request.method == "OPTIONS":
114
+ return Response(status_code=204)
115
+
116
+ body_bytes = await request.body()
117
+ body_json: dict[str, Any] = {}
118
+ if body_bytes:
119
+ try:
120
+ body_json = json.loads(body_bytes.decode("utf-8"))
121
+ except Exception:
122
+ body_json = {}
123
+
124
+ headers = dict(request.headers)
125
+ company = headers.get("x-company") or headers.get("x-organization-name") or "C1"
126
+ project = headers.get("x-project") or "P1"
127
+ user = headers.get("x-user") or headers.get("x-user-email") or "U1"
128
+
129
+ messages = body_json.get("messages", [])
130
+ prompt = ""
131
+ if messages and isinstance(messages, list):
132
+ last_msg = messages[-1]
133
+ if isinstance(last_msg, dict):
134
+ prompt = str(last_msg.get("content", ""))
135
+ elif "prompt" in body_json:
136
+ prompt = str(body_json.get("prompt", ""))
137
+
138
+ if prompt:
139
+ api_payload = {
140
+ "company": company,
141
+ "project": project,
142
+ "user": user,
143
+ "prompt": prompt,
144
+ "model": body_json.get("model"),
145
+ "messages": messages,
146
+ "metadata": {"path": full_path},
147
+ }
148
+ asyncio.create_task(send_event_to_api_subdomain(api_payload))
149
+
150
+ target_url = headers.get("x-target-llm-url") or config.llm_provider_url
151
+ if full_path and not target_url.endswith(full_path):
152
+ if not target_url.endswith("/"):
153
+ target_url = f"{target_url}/{full_path}"
154
+ else:
155
+ target_url = f"{target_url}{full_path}"
156
+
157
+ forward_headers = {
158
+ k: v for k, v in headers.items() if k.lower() not in ("host", "content-length", "x-target-llm-url", "x-company", "x-project", "x-user")
159
+ }
160
+
161
+ try:
162
+ client = httpx.AsyncClient(timeout=120.0)
163
+ req = client.build_request(
164
+ method=request.method,
165
+ url=target_url,
166
+ headers=forward_headers,
167
+ content=body_bytes,
168
+ )
169
+ res = await client.send(req, stream=True)
170
+
171
+ async def stream_response() -> AsyncGenerator[bytes, None]:
172
+ try:
173
+ async for chunk in res.aiter_bytes():
174
+ yield chunk
175
+ finally:
176
+ await res.aclose()
177
+ await client.aclose()
178
+
179
+ return StreamingResponse(
180
+ stream_response(),
181
+ status_code=res.status_code,
182
+ headers={k: v for k, v in res.headers.items() if k.lower() not in ("content-length", "transfer-encoding")},
183
+ )
184
+ except Exception as exc:
185
+ return JSONResponse(
186
+ status_code=502,
187
+ content={"error": f"Local proxy forwarding failed: {str(exc)}"},
188
+ )
189
+
190
+
191
+ def main() -> None:
192
+ import uvicorn
193
+
194
+ uvicorn.run(app, host=config.host, port=config.port)
195
+
196
+
197
+ if __name__ == "__main__":
198
+ main()
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: promptreview-proxy
3
+ Version: 0.1.0
4
+ Summary: Local HTTP proxy forwarding engine for PromptReview VS Code extension with API subdomain analytics.
5
+ Author-email: PromptReview Team <support@prompt-guard-ai.com>
6
+ License: MIT
7
+ Keywords: llm,proxy,fastapi,vscode,promptreview,ai
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Framework :: FastAPI
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: fastapi>=0.110.0
22
+ Requires-Dist: uvicorn>=0.28.0
23
+ Requires-Dist: httpx>=0.27.0
24
+ Requires-Dist: pydantic>=2.6.0
25
+
26
+ # PromptReview Proxy (`promptreview-proxy`)
27
+
28
+ `promptreview-proxy` is a lightweight, local HTTP proxy forwarding server for the PromptReview VS Code Extension.
29
+
30
+ ## Features
31
+ - **Local HTTP Proxying**: Intercepts outbound LLM traffic from VS Code (`http.proxy`).
32
+ - **Cloud Analytics Relay**: Asynchronously logs prompt metadata to your API subdomain (`https://api.prompt-guard-ai.com/v1/prompts`).
33
+ - **Dynamic Provider Routing**: Forwards requests to target LLM providers (OpenAI, Anthropic, Groq, Ollama, etc.) and streams responses back to VS Code.
34
+
35
+ ## Quick Start
36
+
37
+ ### Installation
38
+ ```bash
39
+ pip install promptreview-proxy
40
+ ```
41
+
42
+ ### Usage
43
+ Run the proxy server from command line:
44
+ ```bash
45
+ promptreview-proxy
46
+ ```
47
+
48
+ Or configure environment variables:
49
+ ```bash
50
+ export PROXY_PORT=6767
51
+ export API_BASE_URL="https://api.prompt-guard-ai.com"
52
+ promptreview-proxy
53
+ ```
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ promptreview_proxy/__init__.py
4
+ promptreview_proxy/server.py
5
+ promptreview_proxy.egg-info/PKG-INFO
6
+ promptreview_proxy.egg-info/SOURCES.txt
7
+ promptreview_proxy.egg-info/dependency_links.txt
8
+ promptreview_proxy.egg-info/entry_points.txt
9
+ promptreview_proxy.egg-info/requires.txt
10
+ promptreview_proxy.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ promptreview-proxy = promptreview_proxy.server:main
@@ -0,0 +1,4 @@
1
+ fastapi>=0.110.0
2
+ uvicorn>=0.28.0
3
+ httpx>=0.27.0
4
+ pydantic>=2.6.0
@@ -0,0 +1 @@
1
+ promptreview_proxy
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "promptreview-proxy"
7
+ version = "0.1.0"
8
+ description = "Local HTTP proxy forwarding engine for PromptReview VS Code extension with API subdomain analytics."
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ authors = [{ name = "PromptReview Team", email = "support@prompt-guard-ai.com" }]
12
+ license = { text = "MIT" }
13
+ keywords = ["llm", "proxy", "fastapi", "vscode", "promptreview", "ai"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Environment :: Console",
17
+ "Framework :: FastAPI",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13"
26
+ ]
27
+ dependencies = [
28
+ "fastapi>=0.110.0",
29
+ "uvicorn>=0.28.0",
30
+ "httpx>=0.27.0",
31
+ "pydantic>=2.6.0"
32
+ ]
33
+
34
+ [project.scripts]
35
+ promptreview-proxy = "promptreview_proxy.server:main"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["."]
39
+ include = ["promptreview_proxy*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+