omerehrlich commited on
Commit
7d06b63
Β·
1 Parent(s): 882511f

Require user API key for pipeline + generated-page summaries; keep secret for demo summaries; add OpenAI key guide link

Browse files
api.py CHANGED
@@ -93,13 +93,28 @@ class ClassifyPayload(BaseModel):
93
  api_key: Optional[str] = None
94
 
95
 
96
- def _resolve_api_key(supplied: Optional[str]) -> str:
97
- key = (supplied or "").strip() or os.environ.get("OPENAI_API_KEY", "").strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  if not key:
99
- raise HTTPException(
100
- status_code=400,
101
- detail="No OpenAI API key available β€” none supplied and OPENAI_API_KEY is unset.",
102
- )
 
103
  return key
104
 
105
  @app.post("/prepare")
@@ -158,7 +173,9 @@ async def classify_article(payload: ClassifyPayload, background_tasks: Backgroun
158
  Start classification in the background and return immediately.
159
  Uses the flexible-linker pipeline + OpenAI Batch API (single batch).
160
  """
161
- api_key = _resolve_api_key(payload.api_key)
 
 
162
  model_name = payload.model or "gpt-5-mini"
163
 
164
  df, title = fetch_revision_history(payload.article)
@@ -571,6 +588,11 @@ class SummarizePayload(BaseModel):
571
  section_ids: Optional[List[int]] = None # grouped_idx values; None = all sections
572
  api_key: Optional[str] = None
573
  model: Optional[str] = "gpt-5.4"
 
 
 
 
 
574
  # When the dataset lives only in the user's browser (CSV they uploaded),
575
  # the frontend filters rows itself and sends just the matching explanation
576
  # snippets here. If provided, the server skips its own dataset lookup.
@@ -1043,7 +1065,13 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
1043
 
1044
  try:
1045
  import openai
1046
- client = openai.OpenAI(api_key=_resolve_api_key(payload.api_key))
 
 
 
 
 
 
1047
  completion = client.chat.completions.create(
1048
  model="gpt-5.4",
1049
  messages=[
 
93
  api_key: Optional[str] = None
94
 
95
 
96
+ def _resolve_api_key(
97
+ supplied: Optional[str], allow_secret_fallback: bool = True
98
+ ) -> str:
99
+ """Resolve the OpenAI key to use for a request.
100
+
101
+ When ``allow_secret_fallback`` is True (the demo's summary feature), an
102
+ empty supplied key falls back to the server's ``OPENAI_API_KEY`` secret.
103
+ When False (the user-driven classification pipeline and summaries on a
104
+ generated page), only the user-supplied key is accepted β€” the server
105
+ secret is never used, so the caller must provide their own key.
106
+ """
107
+ supplied_key = (supplied or "").strip()
108
+ if allow_secret_fallback:
109
+ key = supplied_key or os.environ.get("OPENAI_API_KEY", "").strip()
110
+ else:
111
+ key = supplied_key
112
  if not key:
113
+ if allow_secret_fallback:
114
+ detail = "No OpenAI API key available β€” none supplied and OPENAI_API_KEY is unset."
115
+ else:
116
+ detail = "An OpenAI API key is required for this request. Please provide your own key."
117
+ raise HTTPException(status_code=400, detail=detail)
118
  return key
119
 
120
  @app.post("/prepare")
 
173
  Start classification in the background and return immediately.
174
  Uses the flexible-linker pipeline + OpenAI Batch API (single batch).
175
  """
176
+ # The classification pipeline runs on the user's own key β€” never the
177
+ # server secret. The demo path doesn't hit /classify at all.
178
+ api_key = _resolve_api_key(payload.api_key, allow_secret_fallback=False)
179
  model_name = payload.model or "gpt-5-mini"
180
 
181
  df, title = fetch_revision_history(payload.article)
 
588
  section_ids: Optional[List[int]] = None # grouped_idx values; None = all sections
589
  api_key: Optional[str] = None
590
  model: Optional[str] = "gpt-5.4"
591
+ # Whether this summary may use the server's OPENAI_API_KEY secret. True
592
+ # only for the demo pages (their summaries run on the provided secret key).
593
+ # False for summaries on a user-generated page or an uploaded CSV, which
594
+ # must run on the user's own key.
595
+ use_server_key: bool = False
596
  # When the dataset lives only in the user's browser (CSV they uploaded),
597
  # the frontend filters rows itself and sends just the matching explanation
598
  # snippets here. If provided, the server skips its own dataset lookup.
 
1065
 
1066
  try:
1067
  import openai
1068
+ # Demo summaries fall back to the server secret; summaries on a
1069
+ # user-generated page or uploaded CSV require the user's own key.
1070
+ client = openai.OpenAI(
1071
+ api_key=_resolve_api_key(
1072
+ payload.api_key, allow_secret_fallback=payload.use_server_key
1073
+ )
1074
+ )
1075
  completion = client.chat.completions.create(
1076
  model="gpt-5.4",
1077
  messages=[
frontend_build/asset-manifest.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.ccff2033.css",
4
- "main.js": "/static/js/main.65a51a7c.js",
5
  "static/js/206.b8423ec3.chunk.js": "/static/js/206.b8423ec3.chunk.js",
6
  "static/media/KaTeX_AMS-Regular.ttf": "/static/media/KaTeX_AMS-Regular.853be92419a6c3766b9a.ttf",
7
  "static/media/KaTeX_Main-Regular.ttf": "/static/media/KaTeX_Main-Regular.9eba1d77abcf2aa6e94e.ttf",
@@ -67,6 +67,6 @@
67
  },
68
  "entrypoints": [
69
  "static/css/main.ccff2033.css",
70
- "static/js/main.65a51a7c.js"
71
  ]
72
  }
 
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.ccff2033.css",
4
+ "main.js": "/static/js/main.8068e73f.js",
5
  "static/js/206.b8423ec3.chunk.js": "/static/js/206.b8423ec3.chunk.js",
6
  "static/media/KaTeX_AMS-Regular.ttf": "/static/media/KaTeX_AMS-Regular.853be92419a6c3766b9a.ttf",
7
  "static/media/KaTeX_Main-Regular.ttf": "/static/media/KaTeX_Main-Regular.9eba1d77abcf2aa6e94e.ttf",
 
67
  },
68
  "entrypoints": [
69
  "static/css/main.ccff2033.css",
70
+ "static/js/main.8068e73f.js"
71
  ]
72
  }
frontend_build/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#0e7490"/><meta name="description" content="Wikistar β€” understand how scientific revisions evolve over time for a specific Wikipedia page."/><link rel="apple-touch-icon" href="/wikistar-192.png"/><link rel="manifest" href="/manifest.json"/><title>Wikistar β€” Wikipedia Edit History Explorer</title><script defer="defer" src="/static/js/main.65a51a7c.js"></script><link href="/static/css/main.ccff2033.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
 
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#0e7490"/><meta name="description" content="Wikistar β€” understand how scientific revisions evolve over time for a specific Wikipedia page."/><link rel="apple-touch-icon" href="/wikistar-192.png"/><link rel="manifest" href="/manifest.json"/><title>Wikistar β€” Wikipedia Edit History Explorer</title><script defer="defer" src="/static/js/main.8068e73f.js"></script><link href="/static/css/main.ccff2033.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
frontend_build/static/js/{main.65a51a7c.js β†’ main.8068e73f.js} RENAMED
The diff for this file is too large to render. See raw diff
 
frontend_build/static/js/{main.65a51a7c.js.LICENSE.txt β†’ main.8068e73f.js.LICENSE.txt} RENAMED
File without changes