docs: document new github + gitlab ci scripts
This commit is contained in:
parent
437341c9b5
commit
df2a36dd27
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,7 +1,7 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Schedule Demo
|
||||
url: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions
|
||||
url: https://enterprise.litellm.ai/demo
|
||||
about: Speak directly with Krrish and Ishaan, the founders, to discuss issues, share feedback, or explore improvements for LiteLLM
|
||||
- name: Discord
|
||||
url: https://discord.com/invite/wuPM9dRgDw
|
||||
|
||||
2
.github/workflows/interpret_load_test.py
vendored
2
.github/workflows/interpret_load_test.py
vendored
@ -123,7 +123,7 @@ if __name__ == "__main__":
|
||||
+ docker_run_command
|
||||
+ "\n\n"
|
||||
+ "### Don't want to maintain your internal proxy? get in touch 🎉"
|
||||
+ "\nHosted Proxy Alpha: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions"
|
||||
+ "\nHosted Proxy Alpha: https://enterprise.litellm.ai/demo"
|
||||
+ "\n\n"
|
||||
+ "## Load Test LiteLLM Proxy Results"
|
||||
+ "\n\n"
|
||||
|
||||
@ -402,7 +402,7 @@ Support for more providers. Missing a provider or LLM Platform, raise a [feature
|
||||
# Enterprise
|
||||
For companies that need better security, user management and professional support
|
||||
|
||||
[Talk to founders](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Talk to founders](https://enterprise.litellm.ai/demo)
|
||||
|
||||
This covers:
|
||||
- ✅ **Features under the [LiteLLM Commercial License](https://docs.litellm.ai/docs/proxy/enterprise):**
|
||||
|
||||
@ -178,4 +178,4 @@ Benchmark Results for 'When will BerriAI IPO?':
|
||||
```
|
||||
|
||||
## Support
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://enterprise.litellm.ai/demo) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
|
||||
@ -14,7 +14,9 @@ import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
> **Status:** Active investigation
|
||||
> **Last updated:** March 24, 2026, 2:00 PM ET
|
||||
> **Last updated:** March 25, 2026
|
||||
|
||||
> **Update (March 25):** Added community-contributed scripts for scanning GitHub Actions and GitLab CI pipelines for the compromised versions. See [How to check if you are affected](#how-to-check-if-you-are-affected). s/o [@Zach Fury](https://www.linkedin.com/in/fryware/) for these scripts.
|
||||
|
||||
|
||||
## TLDR;
|
||||
@ -91,9 +93,548 @@ pip show litellm
|
||||
Go to the proxy base url, and check the version of the installed LiteLLM.
|
||||
|
||||

|
||||
</TabItem>
|
||||
<TabItem value="github" label="GitHub Actions">
|
||||
|
||||
Scans all repositories in a GitHub organization for workflow jobs that installed the compromised versions.
|
||||
|
||||
**Requirements:** Python 3 and `requests` (`pip install requests`).
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN="your-github-pat"
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
```bash
|
||||
python find_litellm_github.py
|
||||
```
|
||||
|
||||
Set the `ORG` variable in the script to your GitHub organization name.
|
||||
|
||||
Both scripts default to scanning jobs from **today**. Adjust the `WINDOW_START` and `WINDOW_END` constants to cover **March 24, 2026** (the incident date) if running on a different day.
|
||||
|
||||
<details>
|
||||
<summary>View full script (find_litellm_github.py)</summary>
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Scan all GitHub Actions jobs in a GitHub org that ran between
|
||||
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
|
||||
|
||||
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
|
||||
"""
|
||||
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import requests
|
||||
|
||||
GITHUB_URL = "https://api.github.com"
|
||||
ORG = "your-org" # <-- set to your GitHub organization
|
||||
TOKEN = os.environ.get("GITHUB_TOKEN", "")
|
||||
|
||||
TODAY = datetime.now(timezone.utc).date()
|
||||
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
|
||||
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
|
||||
|
||||
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
|
||||
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
|
||||
|
||||
SESSION = requests.Session()
|
||||
SESSION.headers.update({
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
})
|
||||
|
||||
|
||||
def get_paginated(url, params=None):
|
||||
params = dict(params or {})
|
||||
params.setdefault("per_page", 100)
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
if resp.status_code == 404:
|
||||
return
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if isinstance(data, dict):
|
||||
items = next((v for v in data.values() if isinstance(v, list)), [])
|
||||
else:
|
||||
items = data
|
||||
if not items:
|
||||
break
|
||||
yield from items
|
||||
if len(items) < params["per_page"]:
|
||||
break
|
||||
page += 1
|
||||
|
||||
|
||||
def parse_ts(ts_str):
|
||||
if not ts_str:
|
||||
return None
|
||||
return datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
|
||||
|
||||
|
||||
def get_repos():
|
||||
repos = []
|
||||
for r in get_paginated(f"{GITHUB_URL}/orgs/{ORG}/repos", {"type": "all"}):
|
||||
repos.append({"id": r["id"], "name": r["name"], "full_name": r["full_name"]})
|
||||
return repos
|
||||
|
||||
|
||||
def get_runs_in_window(repo_full_name):
|
||||
created_filter = (
|
||||
f"{WINDOW_START.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
||||
f"..{WINDOW_END.strftime('%Y-%m-%dT%H:%M:%SZ')}"
|
||||
)
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs"
|
||||
runs = []
|
||||
for run in get_paginated(url, {"created": created_filter, "per_page": 100}):
|
||||
ts = parse_ts(run.get("run_started_at") or run.get("created_at"))
|
||||
if ts and WINDOW_START <= ts <= WINDOW_END:
|
||||
runs.append(run)
|
||||
return runs
|
||||
|
||||
|
||||
def get_jobs_for_run(repo_full_name, run_id):
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/runs/{run_id}/jobs"
|
||||
jobs = []
|
||||
for job in get_paginated(url, {"filter": "all"}):
|
||||
ts = parse_ts(job.get("started_at"))
|
||||
if ts and WINDOW_START <= ts <= WINDOW_END:
|
||||
jobs.append(job)
|
||||
return jobs
|
||||
|
||||
|
||||
def fetch_job_log(repo_full_name, job_id):
|
||||
url = f"{GITHUB_URL}/repos/{repo_full_name}/actions/jobs/{job_id}/logs"
|
||||
resp = SESSION.get(url, timeout=60, allow_redirects=True)
|
||||
if resp.status_code in (403, 404, 410):
|
||||
return ""
|
||||
resp.raise_for_status()
|
||||
|
||||
content_type = resp.headers.get("Content-Type", "")
|
||||
if "zip" in content_type or resp.content[:2] == b"PK":
|
||||
try:
|
||||
with zipfile.ZipFile(io.BytesIO(resp.content)) as zf:
|
||||
parts = []
|
||||
for name in sorted(zf.namelist()):
|
||||
with zf.open(name) as f:
|
||||
parts.append(f.read().decode("utf-8", errors="replace"))
|
||||
return "\n".join(parts)
|
||||
except zipfile.BadZipFile:
|
||||
pass
|
||||
return resp.text
|
||||
|
||||
|
||||
def check_job(repo_full_name, job):
|
||||
job_id = job["id"]
|
||||
job_name = job["name"]
|
||||
run_id = job["run_id"]
|
||||
started = job.get("started_at", "")
|
||||
|
||||
log_text = fetch_job_log(repo_full_name, job_id)
|
||||
if not log_text:
|
||||
return None
|
||||
|
||||
found_versions = set()
|
||||
context_lines = []
|
||||
for line in log_text.splitlines():
|
||||
m = VERSION_PATTERN.search(line)
|
||||
if m:
|
||||
ver = m.group(1)
|
||||
if ver in TARGET_VERSIONS:
|
||||
found_versions.add(ver)
|
||||
context_lines.append(line.strip())
|
||||
|
||||
if not found_versions:
|
||||
return None
|
||||
|
||||
return {
|
||||
"repo": repo_full_name,
|
||||
"run_id": run_id,
|
||||
"job_id": job_id,
|
||||
"job_name": job_name,
|
||||
"started_at": started,
|
||||
"versions": sorted(found_versions),
|
||||
"context": context_lines[:10],
|
||||
"job_url": job.get("html_url", f"https://github.com/{repo_full_name}/actions/runs/{run_id}"),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print("ERROR: Set GITHUB_TOKEN environment variable.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
|
||||
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
|
||||
print()
|
||||
|
||||
print(f"Fetching repositories for org '{ORG}'...")
|
||||
repos = get_repos()
|
||||
print(f" Found {len(repos)} repositories")
|
||||
print()
|
||||
|
||||
jobs_to_check = []
|
||||
|
||||
print("Scanning workflow runs for time window...")
|
||||
for repo in repos:
|
||||
full_name = repo["full_name"]
|
||||
try:
|
||||
runs = get_runs_in_window(full_name)
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: {full_name} - {e}", file=sys.stderr)
|
||||
continue
|
||||
if not runs:
|
||||
continue
|
||||
print(f" {full_name}: {len(runs)} run(s) in window")
|
||||
for run in runs:
|
||||
try:
|
||||
jobs = get_jobs_for_run(full_name, run["id"])
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: run {run['id']} - {e}", file=sys.stderr)
|
||||
continue
|
||||
for job in jobs:
|
||||
jobs_to_check.append((full_name, job))
|
||||
|
||||
total = len(jobs_to_check)
|
||||
print(f"\nFetching logs for {total} job(s)...")
|
||||
print()
|
||||
|
||||
hits = []
|
||||
with ThreadPoolExecutor(max_workers=8) as pool:
|
||||
futures = {
|
||||
pool.submit(check_job, full_name, job): (full_name, job["id"])
|
||||
for full_name, job in jobs_to_check
|
||||
}
|
||||
done = 0
|
||||
for future in as_completed(futures):
|
||||
done += 1
|
||||
full_name, jid = futures[future]
|
||||
try:
|
||||
result = future.result()
|
||||
except Exception as e:
|
||||
print(f" ERROR {full_name} job {jid}: {e}", file=sys.stderr)
|
||||
continue
|
||||
if result:
|
||||
hits.append(result)
|
||||
print(
|
||||
f" [{done}/{total}] {full_name} job {jid}" +
|
||||
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
|
||||
flush=True,
|
||||
)
|
||||
|
||||
print()
|
||||
print("=" * 72)
|
||||
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
|
||||
print("=" * 72)
|
||||
|
||||
if not hits:
|
||||
print("No matches found.")
|
||||
return
|
||||
|
||||
for h in sorted(hits, key=lambda x: x["started_at"]):
|
||||
print()
|
||||
print(f" Repo : {h['repo']}")
|
||||
print(f" Job : {h['job_name']} (#{h['job_id']})")
|
||||
print(f" Run ID : {h['run_id']}")
|
||||
print(f" Started : {h['started_at']}")
|
||||
print(f" Versions : litellm {', '.join(h['versions'])}")
|
||||
print(f" URL : {h['job_url']}")
|
||||
print(f" Log lines :")
|
||||
for line in h["context"]:
|
||||
print(f" {line}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="gitlab" label="GitLab CI">
|
||||
|
||||
Scans all projects in a GitLab group (including subgroups) for CI/CD jobs that installed the compromised versions.
|
||||
|
||||
**Requirements:** Python 3 and `requests` (`pip install requests`).
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
export GITLAB_TOKEN="your-gitlab-pat"
|
||||
```
|
||||
|
||||
**Run:**
|
||||
|
||||
```bash
|
||||
python find_litellm_jobs.py
|
||||
```
|
||||
|
||||
Set the `GROUP_NAME` variable in the script to your GitLab group name.
|
||||
|
||||
Both scripts default to scanning jobs from **today**. Adjust the `WINDOW_START` and `WINDOW_END` constants to cover **March 24, 2026** (the incident date) if running on a different day.
|
||||
|
||||
<details>
|
||||
<summary>View full script (find_litellm_jobs.py)</summary>
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Scan all GitLab CI/CD jobs in a GitLab group that ran between
|
||||
0800-1244 UTC today and identify any that installed litellm 1.82.7 or 1.82.8.
|
||||
|
||||
Adjust WINDOW_START / WINDOW_END to cover March 24, 2026 if running later.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import requests
|
||||
|
||||
GITLAB_URL = "https://gitlab.com"
|
||||
GROUP_NAME = "YourGroup" # <-- set to your GitLab group name
|
||||
TOKEN = os.environ.get("GITLAB_TOKEN", "")
|
||||
|
||||
TODAY = datetime.now(timezone.utc).date()
|
||||
WINDOW_START = datetime(TODAY.year, TODAY.month, TODAY.day, 8, 0, 0, tzinfo=timezone.utc)
|
||||
WINDOW_END = datetime(TODAY.year, TODAY.month, TODAY.day, 12, 44, 0, tzinfo=timezone.utc)
|
||||
|
||||
TARGET_VERSIONS = {"1.82.7", "1.82.8"}
|
||||
VERSION_PATTERN = re.compile(r"litellm[=\-](\d+\.\d+\.\d+)", re.IGNORECASE)
|
||||
|
||||
HEADERS = {"PRIVATE-TOKEN": TOKEN}
|
||||
SESSION = requests.Session()
|
||||
SESSION.headers.update(HEADERS)
|
||||
|
||||
|
||||
def get_paginated(url, params=None):
|
||||
params = dict(params or {})
|
||||
params.setdefault("per_page", 100)
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
if not data:
|
||||
break
|
||||
yield from data
|
||||
if len(data) < params["per_page"]:
|
||||
break
|
||||
page += 1
|
||||
|
||||
|
||||
def get_group_id(group_name):
|
||||
resp = SESSION.get(f"{GITLAB_URL}/api/v4/groups/{group_name}", timeout=30)
|
||||
resp.raise_for_status()
|
||||
return resp.json()["id"]
|
||||
|
||||
|
||||
def get_all_projects(group_id):
|
||||
projects = []
|
||||
for p in get_paginated(
|
||||
f"{GITLAB_URL}/api/v4/groups/{group_id}/projects",
|
||||
{"include_subgroups": "true", "archived": "false"},
|
||||
):
|
||||
projects.append({"id": p["id"], "name": p["path_with_namespace"]})
|
||||
return projects
|
||||
|
||||
|
||||
def parse_ts(ts_str):
|
||||
if not ts_str:
|
||||
return None
|
||||
ts_str = ts_str.replace("Z", "+00:00")
|
||||
return datetime.fromisoformat(ts_str)
|
||||
|
||||
|
||||
def jobs_in_window(project_id):
|
||||
matching = []
|
||||
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs"
|
||||
params = {"per_page": 100, "scope[]": ["success", "failed", "canceled", "running"]}
|
||||
|
||||
page = 1
|
||||
while True:
|
||||
params["page"] = page
|
||||
resp = SESSION.get(url, params=params, timeout=30)
|
||||
if resp.status_code == 403:
|
||||
return matching
|
||||
resp.raise_for_status()
|
||||
jobs = resp.json()
|
||||
if not jobs:
|
||||
break
|
||||
|
||||
stop_early = False
|
||||
for job in jobs:
|
||||
ts = parse_ts(job.get("started_at") or job.get("created_at"))
|
||||
if ts is None:
|
||||
continue
|
||||
if ts > WINDOW_END:
|
||||
continue
|
||||
if ts < WINDOW_START:
|
||||
stop_early = True
|
||||
continue
|
||||
matching.append(job)
|
||||
|
||||
if stop_early or len(jobs) < 100:
|
||||
break
|
||||
page += 1
|
||||
|
||||
return matching
|
||||
|
||||
|
||||
def fetch_trace(project_id, job_id):
|
||||
url = f"{GITLAB_URL}/api/v4/projects/{project_id}/jobs/{job_id}/trace"
|
||||
resp = SESSION.get(url, timeout=60)
|
||||
if resp.status_code in (403, 404):
|
||||
return ""
|
||||
resp.raise_for_status()
|
||||
return resp.text
|
||||
|
||||
|
||||
def check_job(project_name, project_id, job):
|
||||
job_id = job["id"]
|
||||
job_name = job["name"]
|
||||
ref = job.get("ref", "")
|
||||
started = job.get("started_at", job.get("created_at", ""))
|
||||
|
||||
trace = fetch_trace(project_id, job_id)
|
||||
if not trace:
|
||||
return None
|
||||
|
||||
found_versions = set()
|
||||
for match in VERSION_PATTERN.finditer(trace):
|
||||
ver = match.group(1)
|
||||
if ver in TARGET_VERSIONS:
|
||||
found_versions.add(ver)
|
||||
|
||||
if not found_versions:
|
||||
return None
|
||||
|
||||
context_lines = []
|
||||
for line in trace.splitlines():
|
||||
if VERSION_PATTERN.search(line):
|
||||
ver_match = VERSION_PATTERN.search(line)
|
||||
if ver_match and ver_match.group(1) in TARGET_VERSIONS:
|
||||
context_lines.append(line.strip())
|
||||
|
||||
return {
|
||||
"project": project_name,
|
||||
"project_id": project_id,
|
||||
"job_id": job_id,
|
||||
"job_name": job_name,
|
||||
"ref": ref,
|
||||
"started_at": started,
|
||||
"versions": sorted(found_versions),
|
||||
"context": context_lines[:10],
|
||||
"job_url": f"{GITLAB_URL}/{project_name}/-/jobs/{job_id}",
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
print("ERROR: Set GITLAB_TOKEN environment variable.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Time window : {WINDOW_START.isoformat()} -> {WINDOW_END.isoformat()}")
|
||||
print(f"Hunting for : litellm {', '.join(sorted(TARGET_VERSIONS))}")
|
||||
print()
|
||||
|
||||
print(f"Resolving group '{GROUP_NAME}'...")
|
||||
group_id = get_group_id(GROUP_NAME)
|
||||
|
||||
print("Fetching projects...")
|
||||
projects = get_all_projects(group_id)
|
||||
print(f" Found {len(projects)} projects")
|
||||
print()
|
||||
|
||||
all_jobs_to_check = []
|
||||
|
||||
print("Scanning job listings for time window...")
|
||||
for proj in projects:
|
||||
try:
|
||||
jobs = jobs_in_window(proj["id"])
|
||||
except requests.HTTPError as e:
|
||||
print(f" WARN: {proj['name']} - {e}", file=sys.stderr)
|
||||
continue
|
||||
if jobs:
|
||||
print(f" {proj['name']}: {len(jobs)} job(s) in window")
|
||||
for j in jobs:
|
||||
all_jobs_to_check.append((proj["name"], proj["id"], j))
|
||||
|
||||
total = len(all_jobs_to_check)
|
||||
print(f"\nFetching traces for {total} job(s)...")
|
||||
print()
|
||||
|
||||
hits = []
|
||||
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||
futures = {
|
||||
pool.submit(check_job, pname, pid, job): (pname, job["id"])
|
||||
for pname, pid, job in all_jobs_to_check
|
||||
}
|
||||
done = 0
|
||||
for future in as_completed(futures):
|
||||
done += 1
|
||||
pname, jid = futures[future]
|
||||
try:
|
||||
result = future.result()
|
||||
except Exception as e:
|
||||
print(f" ERROR checking {pname} job {jid}: {e}", file=sys.stderr)
|
||||
continue
|
||||
if result:
|
||||
hits.append(result)
|
||||
print(f" [{done}/{total}] checked {pname} job {jid}" +
|
||||
(f" *** HIT: litellm {result['versions']} ***" if result else ""),
|
||||
flush=True)
|
||||
|
||||
print()
|
||||
print("=" * 72)
|
||||
print(f"RESULTS: {len(hits)} job(s) installed litellm {' or '.join(sorted(TARGET_VERSIONS))}")
|
||||
print("=" * 72)
|
||||
|
||||
if not hits:
|
||||
print("No matches found.")
|
||||
return
|
||||
|
||||
for h in sorted(hits, key=lambda x: x["started_at"]):
|
||||
print()
|
||||
print(f" Project : {h['project']}")
|
||||
print(f" Job : {h['job_name']} (#{h['job_id']})")
|
||||
print(f" Branch/tag: {h['ref']}")
|
||||
print(f" Started : {h['started_at']}")
|
||||
print(f" Versions : litellm {', '.join(h['versions'])}")
|
||||
print(f" URL : {h['job_url']}")
|
||||
print(f" Log lines :")
|
||||
for line in h["context"]:
|
||||
print(f" {line}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
*CI/CD scripts contributed by the community ([original gist](https://gist.github.com/fryz/93ec8d4898ffe5b5ac5706a208823ef3)). Review before running.*
|
||||
|
||||
|
||||
## Indicators of compromise (IoCs)
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ import Image from '@theme/IdealImage';
|
||||
|
||||
:::info
|
||||
- ✨ SSO is free for up to 5 users. After that, an enterprise license is required. [Get Started with Enterprise here](https://www.litellm.ai/enterprise)
|
||||
- Who is Enterprise for? Companies giving access to 100+ users **OR** 10+ AI use-cases. If you're not sure, [get in touch with us](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) to discuss your needs.
|
||||
- Who is Enterprise for? Companies giving access to 100+ users **OR** 10+ AI use-cases. If you're not sure, [get in touch with us](https://enterprise.litellm.ai/demo) to discuss your needs.
|
||||
:::
|
||||
|
||||
For companies that need SSO, user management and professional support for LiteLLM Proxy
|
||||
@ -36,7 +36,7 @@ Manage Yourself - you can deploy our Docker Image or build a custom image from o
|
||||
|
||||
### What’s the cost of the Self-Managed Enterprise edition?
|
||||
|
||||
Self-Managed Enterprise deployments require our team to understand your exact needs. [Get in touch with us to learn more](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Self-Managed Enterprise deployments require our team to understand your exact needs. [Get in touch with us to learn more](https://enterprise.litellm.ai/demo)
|
||||
|
||||
|
||||
### How does deployment with Enterprise License work?
|
||||
@ -106,7 +106,7 @@ Professional Support can assist with LLM/Provider integrations, deployment, upgr
|
||||
|
||||
Pricing is based on usage. We can figure out a price that works for your team, on the call.
|
||||
|
||||
[**Contact Us to learn more**](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[**Contact Us to learn more**](https://enterprise.litellm.ai/demo)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
:::info
|
||||
|
||||
This is an Enterprise only endpoint [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
This is an Enterprise only endpoint [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@ Log LLM Logs to [Google Cloud Storage Buckets](https://cloud.google.com/storage?
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ Use this when you want non-proxy admins to access `/spend` endpoints
|
||||
|
||||
:::info
|
||||
|
||||
Schedule a [meeting with us to get your Enterprise License](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Schedule a [meeting with us to get your Enterprise License](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -203,7 +203,7 @@ After regenerating the key, the user will receive an email notification with:
|
||||
|
||||
:::info
|
||||
|
||||
Customizing Email Branding is an Enterprise Feature [Get in touch with us for a Free Trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Customizing Email Branding is an Enterprise Feature [Get in touch with us for a Free Trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
||||
# ✨ Enterprise Features
|
||||
:::tip
|
||||
|
||||
To get a license, get in touch with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
To get a license, get in touch with us [here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -139,7 +139,7 @@ curl -i http://localhost:4000/v1/chat/completions \
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -417,7 +417,7 @@ curl -i -X POST http://localhost:4000/v1/chat/completions \
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ curl -i http://localhost:4000/v1/chat/completions \
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Contact us to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
:::info
|
||||
|
||||
You need a LiteLLM License to unlock this feature. [Grab time](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions), to get one today!
|
||||
You need a LiteLLM License to unlock this feature. [Grab time](https://enterprise.litellm.ai/demo), to get one today!
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -1109,7 +1109,7 @@ Log LLM Logs to [Google Cloud Storage Buckets](https://cloud.google.com/storage?
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
@ -1194,7 +1194,7 @@ Log LLM Logs/SpendLogs to [Google Cloud Storage PubSub Topic](https://cloud.goog
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
@ -1497,7 +1497,7 @@ Log LLM Logs to [Azure Data Lake Storage](https://learn.microsoft.com/en-us/azur
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ LiteLLM tracks changes to the following entities and actions:
|
||||
|
||||
:::tip
|
||||
|
||||
Requires Enterprise License, Get in touch with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Requires Enterprise License, Get in touch with us [here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Use this if you want to use an Oauth2.0 token to make `/chat`, `/embeddings` req
|
||||
|
||||
:::info
|
||||
|
||||
This is an Enterprise Feature - [get in touch with us if you want a free trial to test if this feature meets your needs]((https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions))
|
||||
This is an Enterprise Feature - [get in touch with us if you want a free trial to test if this feature meets your needs]((https://enterprise.litellm.ai/demo))
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -47,7 +47,7 @@ export LITELLM_LOG="ERROR"
|
||||
|
||||
:::info
|
||||
|
||||
Need Help or want dedicated support ? Talk to a founder [here]: (https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
Need Help or want dedicated support ? Talk to a founder [here]: (https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
:::info
|
||||
|
||||
Requires a LiteLLM Enterprise License. [Get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions).
|
||||
Requires a LiteLLM Enterprise License. [Get a free trial](https://enterprise.litellm.ai/demo).
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -315,7 +315,7 @@ LiteLLM Proxy supports team-based tag routing, allowing you to associate specifi
|
||||
|
||||
:::info
|
||||
|
||||
This is an enterprise feature, [Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
This is an enterprise feature, [Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ Team 3 -> Disabled Logging (for GDPR compliance)
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
@ -248,7 +248,7 @@ Use the `/key/generate` or `/key/update` endpoints to add logging callbacks to a
|
||||
|
||||
:::info
|
||||
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
✨ This is an Enterprise only feature [Get Started with Enterprise here](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
This is an Enterprise feature.
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ Use JWT's to auth admins / users / projects into the proxy.
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import TabItem from '@theme/TabItem';
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ import Image from '@theme/IdealImage';
|
||||
|
||||
[Enterprise Pricing](https://www.litellm.ai/#pricing)
|
||||
|
||||
[Contact us here to get a free trial](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions)
|
||||
[Contact us here to get a free trial](https://enterprise.litellm.ai/demo)
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ Benchmark Results for 'When will BerriAI IPO?':
|
||||
+-----------------+----------------------------------------------------------------------------------+---------------------------+------------+
|
||||
```
|
||||
## Support
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
**🤝 Schedule a 1-on-1 Session:** Book a [1-on-1 session](https://enterprise.litellm.ai/demo) with Krrish and Ishaan, the founders, to discuss any issues, provide feedback, or explore how we can improve LiteLLM for you.
|
||||
|
||||
|
||||
<!--
|
||||
|
||||
@ -7,7 +7,7 @@ With regard to the BerriAI Software:
|
||||
This software and associated documentation files (the "Software") may only be
|
||||
used in production, if you (and any entity that you represent) have agreed to,
|
||||
and are in compliance with, the BerriAI Subscription Terms of Service, available
|
||||
via [call](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions) or email (info@berri.ai) (the "Enterprise Terms"), or other
|
||||
via [call](https://enterprise.litellm.ai/demo) or email (info@berri.ai) (the "Enterprise Terms"), or other
|
||||
agreement governing the use of the Software, as agreed by you and BerriAI,
|
||||
and otherwise have a valid BerriAI Enterprise license for the
|
||||
correct number of user seats. Subject to the foregoing sentence, you are free to
|
||||
|
||||
@ -4,6 +4,6 @@ Code in this folder is licensed under a commercial license. Please review the [L
|
||||
|
||||
**These features are covered under the LiteLLM Enterprise contract**
|
||||
|
||||
👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions?month=2024-02)
|
||||
👉 **Using in an Enterprise / Need specific features ?** Meet with us [here](https://enterprise.litellm.ai/demo?month=2024-02)
|
||||
|
||||
See all Enterprise Features here 👉 [Docs](https://docs.litellm.ai/docs/proxy/enterprise)
|
||||
|
||||
@ -632,7 +632,7 @@ class BaseEmailLogger(CustomLogger):
|
||||
warning_msg = (
|
||||
f"Email sent with default values instead of custom values for: {fields_str}. "
|
||||
"This is an Enterprise feature. To use custom email fields, please upgrade to LiteLLM Enterprise. "
|
||||
"Schedule a meeting here: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions"
|
||||
"Schedule a meeting here: https://enterprise.litellm.ai/demo"
|
||||
)
|
||||
verbose_proxy_logger.warning(f"{warning_msg}")
|
||||
|
||||
|
||||
@ -15,7 +15,18 @@ import inspect
|
||||
import os
|
||||
import secrets
|
||||
from copy import deepcopy
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union, cast
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Dict,
|
||||
List,
|
||||
Literal,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from urllib.parse import urlencode, urlparse
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -338,7 +349,7 @@ async def google_login(
|
||||
total_users = await prisma_client.db.litellm_usertable.count()
|
||||
if total_users and total_users > 5:
|
||||
raise ProxyException(
|
||||
message="You must be a LiteLLM Enterprise user to use SSO for more than 5 users. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
|
||||
message="You must be a LiteLLM Enterprise user to use SSO for more than 5 users. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://enterprise.litellm.ai/demo You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
|
||||
type=ProxyErrorTypes.auth_error,
|
||||
param="premium_user",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
@ -745,7 +756,7 @@ def _handle_generic_sso_error(
|
||||
generic_authorization_endpoint: Optional[str],
|
||||
generic_token_endpoint: Optional[str],
|
||||
additional_headers: dict,
|
||||
) -> None:
|
||||
) -> NoReturn:
|
||||
"""Handle errors from generic SSO verify_and_process. Always re-raises."""
|
||||
error_message = str(e)
|
||||
|
||||
@ -3603,7 +3614,7 @@ async def debug_sso_login(request: Request):
|
||||
):
|
||||
if premium_user is not True:
|
||||
raise ProxyException(
|
||||
message="You must be a LiteLLM Enterprise user to use SSO. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://calendly.com/d/cx9p-5yf-2nm/litellm-introductions You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
|
||||
message="You must be a LiteLLM Enterprise user to use SSO. If you have a license please set `LITELLM_LICENSE` in your env. If you want to obtain a license meet with us here: https://enterprise.litellm.ai/demo You are seeing this error message because You set one of `MICROSOFT_CLIENT_ID`, `GOOGLE_CLIENT_ID`, or `GENERIC_CLIENT_ID` in your env. Please unset this",
|
||||
type=ProxyErrorTypes.auth_error,
|
||||
param="premium_user",
|
||||
code=status.HTTP_403_FORBIDDEN,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user