gateforge-sdk 0.1.0__py3-none-any.whl

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.
gateforge/__init__.py ADDED
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ from gateforge.client import GatforgeClient
4
+ from gateforge.response import GatforgeResponse
5
+
6
+ _client: GatforgeClient | None = None
7
+
8
+
9
+ def init(
10
+ api_key: str,
11
+ openai_key: str | None = None,
12
+ anthropic_key: str | None = None,
13
+ gemini_key: str | None = None,
14
+ ) -> None:
15
+ """
16
+ Initialize Gateforge. Downloads user config from api.gateforge.dev.
17
+ Call once at application startup.
18
+ """
19
+ global _client
20
+ _client = GatforgeClient(
21
+ api_key=api_key,
22
+ openai_key=openai_key,
23
+ anthropic_key=anthropic_key,
24
+ gemini_key=gemini_key,
25
+ )
26
+ _client.load_config()
27
+
28
+
29
+ def chat(
30
+ model: str,
31
+ messages: list[dict],
32
+ provider_key: str | None = None,
33
+ pii_domain: str | None = None,
34
+ prompt_id: str | None = None,
35
+ **kwargs,
36
+ ) -> GatforgeResponse:
37
+ """LLM call with automatic PII masking. All processing is local."""
38
+ if _client is None:
39
+ raise RuntimeError("Call gateforge.init() first")
40
+ return _client.chat(
41
+ model=model,
42
+ messages=messages,
43
+ provider_key=provider_key,
44
+ pii_domain=pii_domain,
45
+ prompt_id=prompt_id,
46
+ **kwargs,
47
+ )
48
+
49
+
50
+ def chat_stream(
51
+ model: str,
52
+ messages: list[dict],
53
+ **kwargs,
54
+ ):
55
+ """Streaming LLM call with automatic PII masking."""
56
+ if _client is None:
57
+ raise RuntimeError("Call gateforge.init() first")
58
+ return _client.chat_stream(model=model, messages=messages, **kwargs)
gateforge/client.py ADDED
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import uuid
4
+
5
+ from gateforge.config import fetch_config, GatforgeConfig
6
+ from gateforge.pii import anonymize_messages, rehydrate_text
7
+ from gateforge.metrics import send_metrics_async
8
+ from gateforge.response import GatforgeResponse
9
+
10
+
11
+ def _model_to_provider(model: str) -> str:
12
+ if model.startswith(("gpt-", "o1", "o3")):
13
+ return "openai"
14
+ if model.startswith("claude"):
15
+ return "anthropic"
16
+ if model.startswith("gemini"):
17
+ return "gemini"
18
+ raise ValueError(
19
+ f"Cannot infer provider from model name '{model}'. "
20
+ "Pass provider_key explicitly or use a model name starting with gpt-, claude, or gemini."
21
+ )
22
+
23
+
24
+ class GatforgeClient:
25
+ def __init__(
26
+ self,
27
+ api_key: str,
28
+ openai_key: str | None = None,
29
+ anthropic_key: str | None = None,
30
+ gemini_key: str | None = None,
31
+ ):
32
+ self.api_key = api_key
33
+ self.provider_keys = {
34
+ "openai": openai_key,
35
+ "anthropic": anthropic_key,
36
+ "gemini": gemini_key,
37
+ }
38
+ self.config: GatforgeConfig = GatforgeConfig()
39
+
40
+ def load_config(self) -> None:
41
+ self.config = fetch_config(self.api_key)
42
+
43
+ def chat(
44
+ self,
45
+ model: str,
46
+ messages: list[dict],
47
+ provider_key: str | None = None,
48
+ pii_domain: str | None = None,
49
+ prompt_id: str | None = None,
50
+ **kwargs,
51
+ ) -> GatforgeResponse:
52
+ provider = _model_to_provider(model)
53
+ key = provider_key or self.provider_keys.get(provider)
54
+ if not key:
55
+ raise ValueError(
56
+ f"No {provider} key provided. Pass it to init() or as provider_key in chat()."
57
+ )
58
+
59
+ domain = pii_domain or self.config.pii_domain
60
+ backend = self.config.pii_backend
61
+
62
+ context = {
63
+ "tenant_id": self.api_key,
64
+ "case_id": prompt_id or "default",
65
+ "thread_id": str(uuid.uuid4()),
66
+ "actor_id": "sdk",
67
+ }
68
+
69
+ # 1. PII masking — local, never leaves the client
70
+ anon_messages, pii_entities = anonymize_messages(messages, domain, backend, context)
71
+
72
+ # 2. Direct LLM call
73
+ if provider == "openai":
74
+ from gateforge.providers.openai import call
75
+ elif provider == "anthropic":
76
+ from gateforge.providers.anthropic import call
77
+ else:
78
+ from gateforge.providers.gemini import call
79
+
80
+ content, meta = call(key, model, anon_messages, **kwargs)
81
+
82
+ # 3. Rehydration — local
83
+ final_content = rehydrate_text(content, domain, backend, context)
84
+
85
+ # 4. Async metrics — metadata only, never content
86
+ send_metrics_async(self.api_key, {
87
+ "model": meta["model"],
88
+ "provider": meta["provider"],
89
+ "prompt_tokens": meta["prompt_tokens"],
90
+ "completion_tokens": meta["completion_tokens"],
91
+ "cost_usd": meta["cost_usd"],
92
+ "latency_ms": meta["latency_ms"],
93
+ "pii_entities": pii_entities,
94
+ "pii_count": len(pii_entities),
95
+ "domain": domain,
96
+ })
97
+
98
+ return GatforgeResponse(
99
+ content=final_content,
100
+ model=meta["model"],
101
+ tokens=meta["total_tokens"],
102
+ prompt_tokens=meta["prompt_tokens"],
103
+ completion_tokens=meta["completion_tokens"],
104
+ cost_usd=meta["cost_usd"],
105
+ latency_ms=meta["latency_ms"],
106
+ pii_detected=pii_entities,
107
+ raw=meta.get("raw", {}),
108
+ )
109
+
110
+ def chat_stream(
111
+ self,
112
+ model: str,
113
+ messages: list[dict],
114
+ pii_domain: str | None = None,
115
+ prompt_id: str | None = None,
116
+ provider_key: str | None = None,
117
+ **kwargs,
118
+ ):
119
+ raise NotImplementedError("Streaming is not yet implemented in the SDK.")
gateforge/config.py ADDED
@@ -0,0 +1,38 @@
1
+ import httpx
2
+ from dataclasses import dataclass, field
3
+
4
+ GATEFORGE_API = "https://api.gateforge.dev"
5
+
6
+
7
+ @dataclass
8
+ class GatforgeConfig:
9
+ pii_domain: str = "generic"
10
+ pii_backend: str = "presidio"
11
+ pii_languages: list[str] = field(default_factory=lambda: ["en"])
12
+ custom_rules: list[dict] = field(default_factory=list)
13
+ default_model: str = "gpt-4o"
14
+
15
+
16
+ def fetch_config(api_key: str) -> GatforgeConfig:
17
+ """
18
+ Downloads user config from api.gateforge.dev.
19
+ Falls back to defaults silently if unreachable.
20
+ """
21
+ try:
22
+ resp = httpx.get(
23
+ f"{GATEFORGE_API}/sdk/config",
24
+ headers={"X-API-Key": api_key},
25
+ timeout=5.0,
26
+ )
27
+ if resp.status_code == 200:
28
+ data = resp.json()
29
+ return GatforgeConfig(
30
+ pii_domain=data.get("pii_domain", "generic"),
31
+ pii_backend=data.get("pii_backend", "presidio"),
32
+ pii_languages=data.get("pii_languages", ["en"]),
33
+ custom_rules=data.get("custom_rules", []),
34
+ default_model=data.get("default_model", "gpt-4o"),
35
+ )
36
+ except Exception:
37
+ pass
38
+ return GatforgeConfig()
gateforge/metrics.py ADDED
@@ -0,0 +1,25 @@
1
+ import threading
2
+ from datetime import datetime, timezone
3
+
4
+ GATEFORGE_API = "https://api.gateforge.dev"
5
+
6
+
7
+ def send_metrics_async(api_key: str, metrics: dict) -> None:
8
+ """
9
+ Sends metadata to api.gateforge.dev in a daemon thread.
10
+ Never sends content — only numeric metadata and entity category lists.
11
+ Never blocks or raises.
12
+ """
13
+ def _send():
14
+ try:
15
+ import httpx
16
+ httpx.post(
17
+ f"{GATEFORGE_API}/sdk/metrics",
18
+ json={**metrics, "ts": datetime.now(timezone.utc).isoformat()},
19
+ headers={"X-API-Key": api_key},
20
+ timeout=3.0,
21
+ )
22
+ except Exception:
23
+ pass
24
+
25
+ threading.Thread(target=_send, daemon=True).start()
gateforge/pii.py ADDED
@@ -0,0 +1,46 @@
1
+ from functools import lru_cache
2
+
3
+
4
+ @lru_cache(maxsize=8)
5
+ def _get_firewall(domain: str, backend: str):
6
+ from privacy_firewall import create_firewall
7
+ return create_firewall(domain, detector_backend=backend)
8
+
9
+
10
+ def anonymize_messages(
11
+ messages: list[dict],
12
+ domain: str,
13
+ backend: str,
14
+ context: dict,
15
+ ) -> tuple[list[dict], list[str]]:
16
+ """
17
+ Anonymizes content of user messages locally.
18
+ Returns (anonymized_messages, deduplicated_entity_types).
19
+ Only role=user messages are processed; system/assistant pass through unchanged.
20
+ """
21
+ fw = _get_firewall(domain, backend)
22
+ anonymized = []
23
+ all_entities: list[str] = []
24
+
25
+ for msg in messages:
26
+ if msg.get("role") == "user" and isinstance(msg.get("content"), str):
27
+ result = fw.anonymize(text=msg["content"], context=context)
28
+ if result.trace and hasattr(result.trace, "detected_entities"):
29
+ for e in result.trace.detected_entities:
30
+ entity_type = (
31
+ e.get("entity_type") if isinstance(e, dict)
32
+ else getattr(e, "entity_type", None)
33
+ )
34
+ if entity_type:
35
+ all_entities.append(entity_type)
36
+ anonymized.append({**msg, "content": result.sanitized_text})
37
+ else:
38
+ anonymized.append(msg)
39
+
40
+ return anonymized, list(set(all_entities))
41
+
42
+
43
+ def rehydrate_text(text: str, domain: str, backend: str, context: dict) -> str:
44
+ """Restores original PII values in LLM response text."""
45
+ fw = _get_firewall(domain, backend)
46
+ return fw.rehydrate(text=text, context=context)
File without changes
@@ -0,0 +1,59 @@
1
+ import time
2
+
3
+ PRICING = {
4
+ "claude-opus-4-8": (0.015, 0.075),
5
+ "claude-opus-4-5": (0.015, 0.075),
6
+ "claude-sonnet-4-6": (0.003, 0.015),
7
+ "claude-sonnet-4-5": (0.003, 0.015),
8
+ "claude-haiku-4-5": (0.0008, 0.004),
9
+ "claude-haiku-4-5-20251001":(0.0008, 0.004),
10
+ "claude-3-5-sonnet-20241022":(0.003, 0.015),
11
+ "claude-3-5-haiku-20241022": (0.0008, 0.004),
12
+ "claude-3-opus-20240229": (0.015, 0.075),
13
+ }
14
+
15
+
16
+ def call(
17
+ anthropic_key: str,
18
+ model: str,
19
+ messages: list[dict],
20
+ **kwargs,
21
+ ) -> tuple[str, dict]:
22
+ import anthropic
23
+
24
+ client = anthropic.Anthropic(api_key=anthropic_key)
25
+
26
+ # Anthropic requires max_tokens; default to 1024 if not provided
27
+ kwargs.setdefault("max_tokens", 1024)
28
+
29
+ t0 = time.perf_counter()
30
+ response = client.messages.create(
31
+ model=model,
32
+ messages=messages,
33
+ **kwargs,
34
+ )
35
+ latency_ms = round((time.perf_counter() - t0) * 1000, 1)
36
+
37
+ usage = response.usage
38
+ prompt_tokens = usage.input_tokens
39
+ completion_tokens = usage.output_tokens
40
+
41
+ input_price, output_price = PRICING.get(model, (0.003, 0.015))
42
+ cost = round(
43
+ (prompt_tokens / 1000 * input_price)
44
+ + (completion_tokens / 1000 * output_price),
45
+ 6,
46
+ )
47
+
48
+ content = response.content[0].text if response.content else ""
49
+
50
+ return content, {
51
+ "model": model,
52
+ "provider": "anthropic",
53
+ "prompt_tokens": prompt_tokens,
54
+ "completion_tokens": completion_tokens,
55
+ "total_tokens": prompt_tokens + completion_tokens,
56
+ "cost_usd": cost,
57
+ "latency_ms": latency_ms,
58
+ "raw": response.model_dump(),
59
+ }
@@ -0,0 +1,70 @@
1
+ import time
2
+
3
+ PRICING = {
4
+ "gemini-2.5-pro": (0.00125, 0.010),
5
+ "gemini-2.5-flash": (0.000075, 0.0003),
6
+ "gemini-2.5-flash-lite": (0.000018, 0.000072),
7
+ "gemini-2.0-flash": (0.0001, 0.0004),
8
+ "gemini-2.0-flash-lite": (0.000018, 0.000072),
9
+ "gemini-1.5-pro": (0.00125, 0.005),
10
+ "gemini-1.5-flash": (0.000075, 0.0003),
11
+ "gemini-1.5-flash-8b": (0.0000375,0.00015),
12
+ }
13
+
14
+
15
+ def call(
16
+ gemini_key: str,
17
+ model: str,
18
+ messages: list[dict],
19
+ **kwargs,
20
+ ) -> tuple[str, dict]:
21
+ from google import genai
22
+
23
+ client = genai.Client(api_key=gemini_key)
24
+
25
+ # Convert OpenAI-style messages to Gemini contents
26
+ contents = _messages_to_contents(messages)
27
+
28
+ t0 = time.perf_counter()
29
+ response = client.models.generate_content(
30
+ model=model,
31
+ contents=contents,
32
+ **kwargs,
33
+ )
34
+ latency_ms = round((time.perf_counter() - t0) * 1000, 1)
35
+
36
+ usage = response.usage_metadata
37
+ prompt_tokens = usage.prompt_token_count or 0
38
+ completion_tokens = usage.candidates_token_count or 0
39
+
40
+ input_price, output_price = PRICING.get(model, (0.000075, 0.0003))
41
+ cost = round(
42
+ (prompt_tokens / 1000 * input_price)
43
+ + (completion_tokens / 1000 * output_price),
44
+ 6,
45
+ )
46
+
47
+ content = response.text or ""
48
+
49
+ return content, {
50
+ "model": model,
51
+ "provider": "gemini",
52
+ "prompt_tokens": prompt_tokens,
53
+ "completion_tokens": completion_tokens,
54
+ "total_tokens": prompt_tokens + completion_tokens,
55
+ "cost_usd": cost,
56
+ "latency_ms": latency_ms,
57
+ "raw": {"text": content, "usage": {"prompt_token_count": prompt_tokens, "candidates_token_count": completion_tokens}},
58
+ }
59
+
60
+
61
+ def _messages_to_contents(messages: list[dict]) -> list:
62
+ """Convert OpenAI chat messages format to Gemini contents list."""
63
+ contents = []
64
+ for msg in messages:
65
+ role = msg.get("role", "user")
66
+ text = msg.get("content", "")
67
+ # Gemini uses "user" and "model" roles
68
+ gemini_role = "model" if role == "assistant" else "user"
69
+ contents.append({"role": gemini_role, "parts": [{"text": text}]})
70
+ return contents
@@ -0,0 +1,50 @@
1
+ import time
2
+
3
+ # Pricing: (input_per_1k, output_per_1k) in USD
4
+ PRICING = {
5
+ "gpt-4o": (0.005, 0.015),
6
+ "gpt-4o-mini": (0.00015, 0.0006),
7
+ "gpt-4.1": (0.002, 0.008),
8
+ "gpt-4.1-mini": (0.0001, 0.0004),
9
+ "gpt-4.1-nano": (0.00005, 0.0002),
10
+ "o1": (0.015, 0.060),
11
+ "o1-mini": (0.003, 0.012),
12
+ "o3-mini": (0.0011, 0.0044),
13
+ }
14
+
15
+
16
+ def call(
17
+ openai_key: str,
18
+ model: str,
19
+ messages: list[dict],
20
+ **kwargs,
21
+ ) -> tuple[str, dict]:
22
+ from openai import OpenAI
23
+
24
+ client = OpenAI(api_key=openai_key)
25
+ t0 = time.perf_counter()
26
+ response = client.chat.completions.create(
27
+ model=model,
28
+ messages=messages,
29
+ **kwargs,
30
+ )
31
+ latency_ms = round((time.perf_counter() - t0) * 1000, 1)
32
+
33
+ usage = response.usage
34
+ input_price, output_price = PRICING.get(model, (0.005, 0.015))
35
+ cost = round(
36
+ (usage.prompt_tokens / 1000 * input_price)
37
+ + (usage.completion_tokens / 1000 * output_price),
38
+ 6,
39
+ )
40
+
41
+ return response.choices[0].message.content, {
42
+ "model": model,
43
+ "provider": "openai",
44
+ "prompt_tokens": usage.prompt_tokens,
45
+ "completion_tokens": usage.completion_tokens,
46
+ "total_tokens": usage.total_tokens,
47
+ "cost_usd": cost,
48
+ "latency_ms": latency_ms,
49
+ "raw": response.model_dump(),
50
+ }
gateforge/response.py ADDED
@@ -0,0 +1,14 @@
1
+ from dataclasses import dataclass, field
2
+
3
+
4
+ @dataclass
5
+ class GatforgeResponse:
6
+ content: str
7
+ model: str
8
+ tokens: int
9
+ prompt_tokens: int
10
+ completion_tokens: int
11
+ cost_usd: float
12
+ latency_ms: float
13
+ pii_detected: list[str]
14
+ raw: dict = field(default_factory=dict)
@@ -0,0 +1,365 @@
1
+ Metadata-Version: 2.4
2
+ Name: gateforge-sdk
3
+ Version: 0.1.0
4
+ Summary: Privacy-first LLMOps SDK — Automatic PII masking, cost tracking, and prompt management for LLM applications
5
+ Project-URL: Homepage, https://gateforge.dev
6
+ Project-URL: Documentation, https://gateforge.dev/docs
7
+ Project-URL: Repository, https://github.com/gateforge/gateforge-sdk
8
+ Project-URL: Dashboard, https://app.gateforge.dev
9
+ Project-URL: Bug Tracker, https://github.com/gateforge/gateforge-sdk/issues
10
+ Author-email: Gateforge Team <support@gateforge.dev>
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: anthropic,gemini,llm,llmops,mlops,openai,pii,privacy
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.27.0
25
+ Requires-Dist: pii-firewall[langdetect,presidio]
26
+ Provides-Extra: all
27
+ Requires-Dist: anthropic>=0.40.0; extra == 'all'
28
+ Requires-Dist: google-genai>=1.0.0; extra == 'all'
29
+ Requires-Dist: openai>=1.0.0; extra == 'all'
30
+ Provides-Extra: anthropic
31
+ Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
32
+ Provides-Extra: dev
33
+ Requires-Dist: build>=1.0.0; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
35
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
36
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
37
+ Provides-Extra: gemini
38
+ Requires-Dist: google-genai>=1.0.0; extra == 'gemini'
39
+ Provides-Extra: openai
40
+ Requires-Dist: openai>=1.0.0; extra == 'openai'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # Gateforge SDK
44
+
45
+ **Privacy-first LLMOps SDK** — Automatic PII masking, cost tracking, and prompt management for LLM applications.
46
+
47
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
48
+ [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
49
+
50
+ ## 🔒 What is Gateforge?
51
+
52
+ Gateforge is an LLMOps platform that automatically protects sensitive data (PII) in your LLM applications. The SDK processes everything **locally** — no content ever leaves your infrastructure.
53
+
54
+ > **Note:** This SDK is free and open source (MIT license). It requires a Gateforge account to use. We offer a generous free tier: **1,000 requests/month**. See [pricing](https://gateforge.dev/pricing) for details.
55
+
56
+ **Key Features:**
57
+ - ✅ **Automatic PII Detection & Masking** — Names, emails, SSN, phone numbers, credit cards, and more
58
+ - ✅ **Multi-Provider Support** — OpenAI, Anthropic, Gemini with unified interface
59
+ - ✅ **Cost Tracking** — Real-time metrics and dashboards
60
+ - ✅ **Domain-Specific Detection** — Healthcare, Finance, Legal, Generic
61
+ - ✅ **Custom Regex Patterns** — Add your own entity types
62
+ - ✅ **Zero Network Overhead** — All PII processing happens locally
63
+
64
+ ## 🚀 Quick Start
65
+
66
+ ### Installation
67
+
68
+ ```bash
69
+ pip install gateforge-sdk
70
+ ```
71
+
72
+ With provider support:
73
+ ```bash
74
+ pip install gateforge-sdk[openai] # OpenAI only
75
+ pip install gateforge-sdk[anthropic] # Anthropic only
76
+ pip install gateforge-sdk[gemini] # Google Gemini only
77
+ pip install gateforge-sdk[all] # All providers
78
+ ```
79
+
80
+ ### Get Your API Key
81
+
82
+ 1. Sign up at [https://gateforge.dev](https://gateforge.dev)
83
+ 2. Get your API key from [https://app.gateforge.dev/dashboard/keys](https://app.gateforge.dev/dashboard/keys)
84
+
85
+ ### Basic Usage
86
+
87
+ ```python
88
+ import gateforge
89
+
90
+ # Initialize once at startup
91
+ gateforge.init(
92
+ api_key="your-gateforge-api-key",
93
+ openai_key="your-openai-key",
94
+ )
95
+
96
+ # Make LLM calls with automatic PII protection
97
+ response = gateforge.chat(
98
+ model="gpt-4.1-nano",
99
+ messages=[
100
+ {"role": "user", "content": "My email is john@example.com, can you help?"}
101
+ ]
102
+ )
103
+
104
+ print(response.content) # "Hello! I'd be happy to help..."
105
+ print(response.pii_detected) # ['EMAIL']
106
+ print(response.tokens) # 45
107
+ print(response.cost_usd) # 0.000023
108
+ ```
109
+
110
+ ## 📖 Examples
111
+
112
+ ### Healthcare Domain
113
+
114
+ ```python
115
+ response = gateforge.chat(
116
+ model="gpt-4.1-nano",
117
+ messages=[
118
+ {
119
+ "role": "user",
120
+ "content": "Patient John Doe, SSN 123-45-6789, has hypertension. Recommend treatment."
121
+ }
122
+ ],
123
+ pii_domain="healthcare" # Detects medical entities
124
+ )
125
+
126
+ print(response.pii_detected) # ['PERSON', 'SSN', 'SYMPTOM']
127
+ ```
128
+
129
+ ### Multiple Providers
130
+
131
+ ```python
132
+ import gateforge
133
+
134
+ gateforge.init(
135
+ api_key="your-gateforge-key",
136
+ openai_key="sk-...",
137
+ anthropic_key="sk-ant-...",
138
+ gemini_key="...",
139
+ )
140
+
141
+ # OpenAI
142
+ r1 = gateforge.chat(model="gpt-4.1-nano", messages=[...])
143
+
144
+ # Anthropic
145
+ r2 = gateforge.chat(model="claude-haiku-4-5", messages=[...])
146
+
147
+ # Gemini
148
+ r3 = gateforge.chat(model="gemini-2.5-flash", messages=[...])
149
+ ```
150
+
151
+ ### Custom PII Patterns
152
+
153
+ Configure custom patterns in your dashboard at [https://app.gateforge.dev/dashboard/pii/settings](https://app.gateforge.dev/dashboard/pii/settings), and the SDK will download them automatically.
154
+
155
+ ## 🔧 Configuration
156
+
157
+ ### Initialization Options
158
+
159
+ ```python
160
+ gateforge.init(
161
+ api_key="your-gateforge-api-key", # Required
162
+ openai_key="sk-...", # Optional
163
+ anthropic_key="sk-ant-...", # Optional
164
+ gemini_key="...", # Optional
165
+ )
166
+ ```
167
+
168
+ ### Chat Parameters
169
+
170
+ ```python
171
+ response = gateforge.chat(
172
+ model="gpt-4.1-nano", # Required: model name
173
+ messages=[...], # Required: chat messages
174
+ pii_domain="generic", # Optional: "generic", "healthcare", "finance", "legal"
175
+ provider_key="sk-...", # Optional: override provider key
176
+ prompt_id="user-signup", # Optional: for prompt tracking
177
+ temperature=0.7, # Optional: model parameters
178
+ max_tokens=500, # Optional: model parameters
179
+ )
180
+ ```
181
+
182
+ ### Response Object
183
+
184
+ ```python
185
+ response = gateforge.chat(...)
186
+
187
+ response.content # str: The response content
188
+ response.pii_detected # list[str]: Detected PII types
189
+ response.tokens # int: Total tokens
190
+ response.prompt_tokens # int: Input tokens
191
+ response.completion_tokens # int: Output tokens
192
+ response.cost_usd # float: Cost in USD
193
+ response.latency_ms # float: Latency in milliseconds
194
+ response.model # str: Model used
195
+ response.raw # dict: Raw provider response
196
+ ```
197
+
198
+ ## 🔒 Privacy & Security
199
+
200
+ ### How it Works
201
+
202
+ 1. **Local Processing**: All PII detection happens locally in your environment
203
+ 2. **Content Never Sent**: Only metadata (tokens, cost) is sent to Gateforge API
204
+ 3. **Automatic Masking**: PII is replaced with tokens before sending to LLM providers
205
+ 4. **Automatic Rehydration**: Responses are reconstructed with original context
206
+
207
+ ### What Gets Detected?
208
+
209
+ - **Personal**: Names, emails, phone numbers, addresses
210
+ - **Financial**: Credit cards, bank accounts, SSN, tax IDs
211
+ - **Healthcare**: Medical record numbers, symptoms, diagnoses
212
+ - **Technical**: IP addresses, URLs, API keys
213
+ - **Custom**: Your own regex patterns
214
+
215
+ ### Supported Backends
216
+
217
+ - **Presidio** (default): ML-based, highest accuracy
218
+ - **Regex**: Pattern matching, fastest
219
+ - **Hybrid**: Combined ML + regex
220
+ - **GLiNER**: Zero-shot NER
221
+ - **Transformers**: HuggingFace models
222
+
223
+ ## 📊 Dashboard & Monitoring
224
+
225
+ View real-time metrics at [https://app.gateforge.dev/dashboard](https://app.gateforge.dev/dashboard):
226
+
227
+ - 📈 Request volume and trends
228
+ - 💰 Cost breakdown by model and provider
229
+ - ⚡ Latency analytics
230
+ - 🔒 PII detection statistics
231
+ - 🔑 API key management
232
+
233
+ ## 🛠️ Advanced Usage
234
+
235
+ ### Streaming (Coming Soon)
236
+
237
+ ```python
238
+ for chunk in gateforge.chat_stream(model="gpt-4.1-nano", messages=[...]):
239
+ print(chunk.content, end="", flush=True)
240
+ ```
241
+
242
+ ### Async Support (Coming Soon)
243
+
244
+ ```python
245
+ response = await gateforge.achat(model="gpt-4.1-nano", messages=[...])
246
+ ```
247
+
248
+ ### Direct Anonymization
249
+
250
+ ```python
251
+ from gateforge.pii import anonymize_messages
252
+
253
+ context = {
254
+ "tenant_id": "user-123",
255
+ "case_id": "case-456",
256
+ "thread_id": "thread-789",
257
+ "actor_id": "user"
258
+ }
259
+
260
+ anonymized, entities = anonymize_messages(
261
+ messages=[{"role": "user", "content": "My email is john@example.com"}],
262
+ domain="generic",
263
+ backend="presidio",
264
+ context=context
265
+ )
266
+
267
+ print(anonymized) # [{"role": "user", "content": "My email is [EMAIL_001]"}]
268
+ print(entities) # ['EMAIL']
269
+ ```
270
+
271
+ ## 🌐 Supported Models
272
+
273
+ ### OpenAI
274
+ - GPT-4.1-nano, GPT-4.1-mini, GPT-4.1, GPT-4.1-turbo
275
+ - GPT-4o, GPT-4o-mini
276
+ - GPT-3.5-turbo
277
+
278
+ ### Anthropic
279
+ - Claude Haiku 4-5
280
+ - Claude Sonnet 4-5
281
+ - Claude Opus 4
282
+
283
+ ### Google Gemini
284
+ - Gemini 2.5 Flash
285
+ - Gemini 2.5 Pro
286
+
287
+ ## 📝 Configuration via Dashboard
288
+
289
+ ### PII Settings
290
+
291
+ 1. Go to [https://app.gateforge.dev/dashboard/pii/settings](https://app.gateforge.dev/dashboard/pii/settings)
292
+ 2. Select domain: Generic, Healthcare, Finance, Legal
293
+ 3. Choose backend: Presidio, Regex, Hybrid
294
+ 4. Set language: Auto-detect or specific language
295
+ 5. Add custom regex patterns
296
+
297
+ The SDK downloads your configuration automatically on init.
298
+
299
+ ### Custom Patterns
300
+
301
+ Add patterns like:
302
+ ```
303
+ Entity Type: EMPLOYEE_ID
304
+ Regex: EMP-\d{6}
305
+ Confidence: 0.95
306
+ ```
307
+
308
+ ## 🐛 Troubleshooting
309
+
310
+ ### ImportError: No module named 'gateforge'
311
+
312
+ ```bash
313
+ pip install gateforge-sdk
314
+ ```
315
+
316
+ ### RuntimeError: Call gateforge.init() first
317
+
318
+ Make sure to initialize before making calls:
319
+ ```python
320
+ gateforge.init(api_key="your-key")
321
+ ```
322
+
323
+ ### API Key Not Working
324
+
325
+ 1. Verify your key at [https://app.gateforge.dev/dashboard/keys](https://app.gateforge.dev/dashboard/keys)
326
+ 2. Check it's not revoked
327
+ 3. Ensure you're using the correct environment
328
+
329
+ ### PII Not Detected
330
+
331
+ 1. Check your domain setting matches your use case
332
+ 2. Try different backends (presidio is most accurate)
333
+ 3. Add custom patterns for domain-specific entities
334
+
335
+ ## 📚 Documentation
336
+
337
+ - **Website**: [https://gateforge.dev](https://gateforge.dev)
338
+ - **Dashboard**: [https://app.gateforge.dev](https://app.gateforge.dev)
339
+ - **API Docs**: [https://api.gateforge.dev/docs](https://api.gateforge.dev/docs)
340
+
341
+ ## 🤝 Contributing
342
+
343
+ Contributions are welcome! Please open an issue or submit a pull request.
344
+
345
+ ## 📄 License
346
+
347
+ MIT License - See [LICENSE](LICENSE) for details.
348
+
349
+ **Important:** While the SDK is open source, the Gateforge service is commercial. You need a Gateforge account (free tier available) to use this SDK.
350
+
351
+ ## 🔗 Links
352
+
353
+ - [Website](https://gateforge.dev)
354
+ - [Dashboard](https://app.gateforge.dev)
355
+ - [API Health](https://api.gateforge.dev/v1/health)
356
+ - [Documentation](https://gateforge.dev/docs)
357
+
358
+ ## 💬 Support
359
+
360
+ - **Email**: support@gateforge.dev
361
+ - **Issues**: [GitHub Issues](https://github.com/gateforge/gateforge-sdk/issues)
362
+
363
+ ---
364
+
365
+ **Made with ❤️ by the Gateforge team**
@@ -0,0 +1,14 @@
1
+ gateforge/__init__.py,sha256=jlIhvOjDeNBF6pq4-0So2hPir4AZXwB4DJD0SBdWXso,1480
2
+ gateforge/client.py,sha256=tgSQjGDzVr7J6fUZoxQJbxFtfMBsEtd9FUbdR5Fkkno,3866
3
+ gateforge/config.py,sha256=0She_mox_G8B3g0iBr4CdDCblMgfKEdnmur2bTb5fQo,1186
4
+ gateforge/metrics.py,sha256=k3EdEBDqzRhE4i5KTWImvlMwfMWt0xV4Nem0I9gD-IE,744
5
+ gateforge/pii.py,sha256=G4QHdweuWlOyLLEh5hfSv9Di29qK-VzNCO7OnbVMe1k,1651
6
+ gateforge/response.py,sha256=XrBKaGlfPbVgpX_ozhHusK0bb8GRpCU29Z5gQ92eNCE,290
7
+ gateforge/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ gateforge/providers/anthropic.py,sha256=XpU6TXODaw3oGDPJaRuHYcEpEDqRAHkPHY3_sNZ8wVo,1695
9
+ gateforge/providers/gemini.py,sha256=N1p6m0JJGXQos4Gu4WoYLJvoILgCjda2TkKFBBVMmrY,2197
10
+ gateforge/providers/openai.py,sha256=cODmvE2lQRWwbFRXcTDEhP_e0vw3ZmCEZaJYW-KpYvo,1384
11
+ gateforge_sdk-0.1.0.dist-info/METADATA,sha256=RxiZweYywfHyHHbwMMhb0he6yGhm4JP_5_xvhK1fw68,10885
12
+ gateforge_sdk-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
13
+ gateforge_sdk-0.1.0.dist-info/licenses/LICENSE,sha256=1BtvvSjjJj2tVyp3LZfuNwrzoTkOcqm5O7n3jagI6uA,1629
14
+ gateforge_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,37 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gateforge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ IMPORTANT NOTICE:
26
+
27
+ This SDK requires a Gateforge account and API key to function.
28
+ Service usage is subject to Gateforge's Terms of Service and Privacy Policy:
29
+ - Terms of Service: https://gateforge.dev/terms
30
+ - Privacy Policy: https://gateforge.dev/privacy
31
+ - Pricing: https://gateforge.dev/pricing
32
+
33
+ The Gateforge service (api.gateforge.dev) is a commercial SaaS offering
34
+ operated by Gateforge. Different pricing plans and usage limits apply.
35
+
36
+ Free tier: 1,000 requests/month
37
+ See https://gateforge.dev/pricing for details.