diff --git a/fastapi_server/front_apis.py b/fastapi_server/front_apis.py index 035b7ac..daf0ac7 100644 --- a/fastapi_server/front_apis.py +++ b/fastapi_server/front_apis.py @@ -22,6 +22,7 @@ class GraphConfigUpsertRequest(BaseModel): prompt_set_id: Optional[str] = Field(default=None) tool_keys: List[str] = Field(default_factory=list) prompt_dict: Dict[str, str] = Field(default_factory=dict) + api_key: Optional[str] = Field(default=None) class GraphConfigUpsertResponse(BaseModel): graph_id: str @@ -29,6 +30,7 @@ class GraphConfigUpsertResponse(BaseModel): prompt_set_id: str tool_keys: List[str] prompt_keys: List[str] + api_key: str class GraphConfigReadResponse(BaseModel): graph_id: Optional[str] = Field(default=None) @@ -36,6 +38,7 @@ class GraphConfigReadResponse(BaseModel): prompt_set_id: str tool_keys: List[str] prompt_dict: Dict[str, str] + api_key: str = Field(default="") class GraphConfigListItem(BaseModel): graph_id: Optional[str] = Field(default=None) @@ -45,6 +48,7 @@ class GraphConfigListItem(BaseModel): description: str is_active: bool tool_keys: List[str] + api_key: str = Field(default="") created_at: Optional[str] = Field(default=None) updated_at: Optional[str] = Field(default=None) @@ -169,6 +173,7 @@ async def upsert_graph_config(body: GraphConfigUpsertRequest): prompt_set_id=body.prompt_set_id, tool_list=body.tool_keys, prompt_dict=body.prompt_dict, + api_key=body.api_key, ) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) @@ -181,6 +186,7 @@ async def upsert_graph_config(body: GraphConfigUpsertRequest): prompt_set_id=resolved_prompt_set_id, tool_keys=body.tool_keys, prompt_keys=list(body.prompt_dict.keys()), + api_key=(body.api_key or "").strip(), ) @app.get("/v1/graph-configs", response_model=GraphConfigListResponse) @@ -222,6 +228,7 @@ async def get_default_graph_config(pipeline_id: str): prompt_set_id=active["prompt_set_id"], tool_keys=tool_keys, prompt_dict=prompt_dict, + api_key=(active.get("api_key") or ""), ) @app.get("/v1/graphs/{graph_id}/default-config", response_model=GraphConfigReadResponse) @@ -254,6 +261,7 @@ async def get_graph_config(pipeline_id: str, prompt_set_id: str): prompt_set_id=prompt_set_id, tool_keys=tool_keys, prompt_dict=prompt_dict, + api_key=(meta.get("api_key") or ""), ) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 48c82b9..95e57f4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -101,7 +101,7 @@ function toEditable( toolKeys: config.tool_keys || [], prompts: config.prompt_dict || {}, port: DEFAULT_PORT, - apiKey: DEFAULT_API_KEY, + apiKey: config.api_key || DEFAULT_API_KEY, llmName: DEFAULT_LLM_NAME, }; } @@ -207,8 +207,8 @@ export default function App() { const editable = toEditable(detail, false); editable.id = id; editable.port = editor?.pipelineId === editable.pipelineId ? editor.port : DEFAULT_PORT; - editable.apiKey = editor?.pipelineId === editable.pipelineId ? editor.apiKey : DEFAULT_API_KEY; editable.llmName = editor?.pipelineId === editable.pipelineId ? editor.llmName : DEFAULT_LLM_NAME; + // apiKey is loaded from backend (persisted in DB) — don't override with default setEditor(editable); setStatusMessage(""); } catch (error) { @@ -305,6 +305,7 @@ export default function App() { prompt_set_id: "default", tool_keys: [], prompt_dict: fallbackPrompts, + api_key: "", }; } } @@ -340,6 +341,7 @@ export default function App() { prompt_set_id: editor.promptSetId, tool_keys: editor.toolKeys, prompt_dict: editor.prompts, + api_key: editor.apiKey.trim(), }); await refreshConfigs(); @@ -347,7 +349,7 @@ export default function App() { const saved = toEditable(detail, false); saved.id = makeAgentKey(upsertResp.pipeline_id, upsertResp.prompt_set_id); saved.port = editor.port; - saved.apiKey = editor.apiKey; + // apiKey is loaded from backend (persisted in DB) — don't override saved.llmName = editor.llmName; setEditor(saved); setSelectedId(saved.id); diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 1ded571..9b96dce 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -6,6 +6,7 @@ export type GraphConfigListItem = { description: string; is_active: boolean; tool_keys: string[]; + api_key: string; created_at?: string | null; updated_at?: string | null; }; @@ -21,6 +22,7 @@ export type GraphConfigReadResponse = { prompt_set_id: string; tool_keys: string[]; prompt_dict: Record; + api_key: string; }; export type GraphConfigUpsertRequest = { @@ -29,6 +31,7 @@ export type GraphConfigUpsertRequest = { prompt_set_id?: string; tool_keys: string[]; prompt_dict: Record; + api_key?: string; }; export type GraphConfigUpsertResponse = { @@ -37,6 +40,7 @@ export type GraphConfigUpsertResponse = { prompt_set_id: string; tool_keys: string[]; prompt_keys: string[]; + api_key: string; }; export type AvailableGraphsResponse = { diff --git a/lang_agent/config/db_config_manager.py b/lang_agent/config/db_config_manager.py index e40102c..a5d4d20 100644 --- a/lang_agent/config/db_config_manager.py +++ b/lang_agent/config/db_config_manager.py @@ -28,7 +28,7 @@ class DBConfigManager: if pipeline_id and graph_id: cur.execute( """ - SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list + SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list, api_key FROM prompt_sets WHERE pipeline_id = %s AND graph_id = %s ORDER BY updated_at DESC, created_at DESC @@ -38,7 +38,7 @@ class DBConfigManager: elif pipeline_id: cur.execute( """ - SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list + SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list, api_key FROM prompt_sets WHERE pipeline_id = %s ORDER BY updated_at DESC, created_at DESC @@ -48,7 +48,7 @@ class DBConfigManager: elif graph_id: cur.execute( """ - SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list + SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list, api_key FROM prompt_sets WHERE graph_id = %s ORDER BY updated_at DESC, created_at DESC @@ -58,7 +58,7 @@ class DBConfigManager: else: cur.execute( """ - SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list + SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list, api_key FROM prompt_sets ORDER BY updated_at DESC, created_at DESC """ @@ -76,6 +76,7 @@ class DBConfigManager: "created_at": row["created_at"].isoformat() if row["created_at"] else None, "updated_at": row["updated_at"].isoformat() if row["updated_at"] else None, "tool_keys": self._parse_tool_list(row.get("list")), + "api_key": row.get("api_key") or "", } for row in rows ] @@ -88,7 +89,7 @@ class DBConfigManager: with conn.cursor(row_factory=dict_row) as cur: cur.execute( """ - SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list + SELECT id, pipeline_id, graph_id, name, description, is_active, created_at, updated_at, list, api_key FROM prompt_sets WHERE id = %s AND pipeline_id = %s """, @@ -109,6 +110,7 @@ class DBConfigManager: "created_at": row["created_at"].isoformat() if row["created_at"] else None, "updated_at": row["updated_at"].isoformat() if row["updated_at"] else None, "tool_keys": self._parse_tool_list(row.get("list")), + "api_key": row.get("api_key") or "", } def get_config( @@ -160,6 +162,7 @@ class DBConfigManager: prompt_set_id: Optional[str], tool_list: Optional[Sequence[str]], prompt_dict: Optional[Mapping[str, str]], + api_key: Optional[str] = None, ) -> str: """ Persist prompt + tool configuration. @@ -182,6 +185,7 @@ class DBConfigManager: normalized_prompt_dict = self._normalize_prompt_dict(prompt_dict) tool_csv = self._join_tool_list(tool_list) + normalized_api_key = self._normalize_api_key(api_key) with psycopg.connect(self.conn_str) as conn: resolved_set_id, _ = self._resolve_prompt_set( @@ -200,10 +204,13 @@ class DBConfigManager: cur.execute( """ UPDATE prompt_sets - SET list = %s, graph_id = COALESCE(%s, graph_id), updated_at = now() + SET list = %s, + graph_id = COALESCE(%s, graph_id), + api_key = COALESCE(%s, api_key), + updated_at = now() WHERE id = %s """, - (tool_csv, normalized_graph_id, resolved_set_id), + (tool_csv, normalized_graph_id, normalized_api_key, resolved_set_id), ) keys = list(normalized_prompt_dict.keys()) @@ -340,4 +347,9 @@ class DBConfigManager: if graph_id is None: return None value = str(graph_id).strip() - return value or None \ No newline at end of file + return value or None + + def _normalize_api_key(self, api_key: Optional[str]) -> Optional[str]: + if api_key is None: + return None + return str(api_key).strip() \ No newline at end of file