jayanth922 commited on
Commit
5332e69
·
1 Parent(s): fcc2e99

chore: remove optional Modal job and VS Code tasks

Browse files
Files changed (2) hide show
  1. .vscode/tasks.json +0 -47
  2. modal/spec_refresh.py +0 -117
.vscode/tasks.json DELETED
@@ -1,47 +0,0 @@
1
- {
2
- "version": "2.0.0",
3
- "tasks": [
4
- {
5
- "label": "Run (httpbin profile)",
6
- "type": "shell",
7
- "command": "${workspaceFolder}/.venv/bin/python",
8
- "args": ["app.py", "--profile", "httpbin"],
9
- "options": { "cwd": "${workspaceFolder}" },
10
- "problemMatcher": []
11
- },
12
- {
13
- "label": "Run (petstore3 profile)",
14
- "type": "shell",
15
- "command": "${workspaceFolder}/.venv/bin/python",
16
- "args": ["app.py", "--profile", "petstore3"],
17
- "options": { "cwd": "${workspaceFolder}" },
18
- "problemMatcher": []
19
- },
20
- {
21
- "label": "Run (custom flags)",
22
- "type": "shell",
23
- "command": "${workspaceFolder}/.venv/bin/python",
24
- "args": [
25
- "app.py",
26
- "--spec","${input:spec}",
27
- "--base-url","${input:base}",
28
- "--bearer","${input:bearer}"
29
- ],
30
- "options": { "cwd": "${workspaceFolder}" },
31
- "problemMatcher": []
32
- },
33
- {
34
- "label": "Run (hot reload via gradio CLI)",
35
- "type": "shell",
36
- "command": "${workspaceFolder}/.venv/bin/gradio",
37
- "args": ["app.py"],
38
- "options": { "cwd": "${workspaceFolder}" },
39
- "problemMatcher": []
40
- }
41
- ],
42
- "inputs": [
43
- { "id":"spec", "type":"promptString", "description":"OpenAPI spec URL/path", "default":"https://petstore3.swagger.io/api/v3/openapi.json" },
44
- { "id":"base", "type":"promptString", "description":"Base URL", "default":"https://petstore3.swagger.io/api/v3" },
45
- { "id":"bearer", "type":"promptString", "description":"Bearer token (optional)", "default":"" }
46
- ]
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modal/spec_refresh.py DELETED
@@ -1,117 +0,0 @@
1
- # modal/spec_refresh.py
2
- from __future__ import annotations
3
- import os, json, time, yaml, requests
4
- from pathlib import Path
5
- import modal
6
-
7
- PROFILES_REMOTE_PATH = "/workspace/profiles.yaml" # baked into the image
8
-
9
- # Build container image with deps + bake profiles.yaml into it
10
- image = (
11
- modal.Image.debian_slim()
12
- .pip_install(
13
- "requests",
14
- "pyyaml",
15
- "openapi-spec-validator",
16
- "fastapi[standard]" # required for @fastapi_endpoint
17
- )
18
- .add_local_file("profiles.yaml", remote_path=PROFILES_REMOTE_PATH) # ⬅️ no Mounts
19
- )
20
-
21
- app = modal.App(name="anyapi-mcp-spec-refresh", image=image)
22
-
23
- # Durable cache for validated specs
24
- VOLUME_NAME = "anyapi-oas-cache"
25
- volume = modal.Volume.from_name(VOLUME_NAME, create_if_missing=True)
26
- CACHE_DIR = Path("/cache")
27
-
28
- # ---------- helpers ----------
29
- def _load_profiles() -> dict:
30
- if not os.path.exists(PROFILES_REMOTE_PATH):
31
- # Keep graceful behavior if file missing
32
- return {}
33
- with open(PROFILES_REMOTE_PATH, "r", encoding="utf-8") as f:
34
- return yaml.safe_load(f) or {}
35
-
36
- def _validate_oas(spec_obj: dict) -> tuple[bool, str]:
37
- from openapi_spec_validator import validate_spec
38
- try:
39
- validate_spec(spec_obj)
40
- return True, "ok"
41
- except Exception as e:
42
- return False, f"{type(e).__name__}: {e}"
43
-
44
- def _coerce_yaml_or_json(text: str) -> dict:
45
- try:
46
- return json.loads(text)
47
- except json.JSONDecodeError:
48
- return yaml.safe_load(text)
49
-
50
- def _now_iso() -> str:
51
- return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
52
-
53
- # ---------- job: refresh all ----------
54
- @app.function(
55
- image=image,
56
- volumes={CACHE_DIR: volume},
57
- schedule=modal.Cron("0 */6 * * *", timezone="America/Los_Angeles"),
58
- retries=modal.Retries(max_retries=2),
59
- )
60
- def refresh_all():
61
- profiles = _load_profiles()
62
- results = {}
63
- for name, cfg in (profiles or {}).items():
64
- spec_url = (cfg or {}).get("spec")
65
- base_url = (cfg or {}).get("base_url")
66
- if not spec_url:
67
- results[name] = {"ok": False, "error": "missing spec url"}
68
- continue
69
- try:
70
- r = requests.get(spec_url, timeout=30)
71
- r.raise_for_status()
72
- spec_obj = _coerce_yaml_or_json(r.text)
73
- ok, msg = _validate_oas(spec_obj)
74
- except Exception as e:
75
- ok, msg, spec_obj = False, f"{type(e).__name__}: {e}", None
76
-
77
- prof_dir = CACHE_DIR / name
78
- prof_dir.mkdir(parents=True, exist_ok=True)
79
- meta = {
80
- "profile": name,
81
- "spec_url": spec_url,
82
- "base_url": base_url,
83
- "last_checked": _now_iso(),
84
- "ok": ok,
85
- "message": msg,
86
- }
87
- (prof_dir / "meta.json").write_text(json.dumps(meta, indent=2), encoding="utf-8")
88
- if ok and isinstance(spec_obj, dict):
89
- (prof_dir / "openapi.json").write_text(json.dumps(spec_obj, indent=2), encoding="utf-8")
90
- results[name] = meta
91
-
92
- (CACHE_DIR / "index.json").write_text(json.dumps(results, indent=2), encoding="utf-8")
93
- volume.commit()
94
- return results
95
-
96
- # one-off local run
97
- @app.local_entrypoint()
98
- def run_once():
99
- out = refresh_all.remote()
100
- print(json.dumps(out, indent=2))
101
-
102
- # ---------- tiny web endpoints ----------
103
- @app.function(image=image, volumes={CACHE_DIR: volume})
104
- @modal.fastapi_endpoint(docs=True)
105
- def status():
106
- idx = CACHE_DIR / "index.json"
107
- if idx.exists():
108
- return json.loads(idx.read_text(encoding="utf-8"))
109
- return {"error": "no runs yet"}
110
-
111
- @app.function(image=image, volumes={CACHE_DIR: volume})
112
- @modal.fastapi_endpoint()
113
- def download(profile: str):
114
- p = CACHE_DIR / profile / "openapi.json"
115
- if not p.exists():
116
- return {"error": f"no spec for profile '{profile}' (run refresh or check status)"}
117
- return json.loads(p.read_text(encoding="utf-8"))