{"record":{"id":"e0017bbdb88c01bb","repo":"infiniflow/ragflow","slug":"keenable-realtime-mode-requires-an-api-key","errorCode":null,"errorMessage":"Keenable 'realtime' mode requires an API key","messagePattern":"Keenable 'realtime' mode requires an API key","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/tools/keenable.py","lineNumber":103,"sourceCode":"                    \"required\": False,\n                },\n            },\n        }\n        super().__init__()\n        # A key is optional: blank uses the keyless public endpoint (free tier);\n        # setting one lifts rate limits and enables the 'realtime' mode.\n        self.api_key = \"\"\n        # \"pro\" (default, deeper) or \"realtime\" (low latency; requires a key).\n        self.mode = \"pro\"\n        self.top_n = 10\n\n    def check(self):\n        self.check_valid_value(self.mode, \"Keenable search mode should be in 'pro/realtime'\", [\"pro\", \"realtime\"])\n        self.check_positive_integer(self.top_n, \"Top N\")\n        # 'realtime' is not available on the keyless public endpoint, so reject\n        # the invalid combination at config time instead of failing at runtime.\n        if self.mode == \"realtime\" and not (self.api_key or \"\").strip():\n            raise ValueError(\"Keenable 'realtime' mode requires an API key\")\n\n    def get_input_form(self) -> dict[str, dict]:\n        return {\n            \"query\": {\n                \"name\": \"Query\",\n                \"type\": \"line\",\n            },\n            \"site\": {\n                \"name\": \"Site\",\n                \"type\": \"line\",\n            },\n        }\n\n\nclass KeenableSearch(ToolBase, ABC):\n    component_name = \"KeenableSearch\"\n\n    @timeout(int(os.environ.get(\"COMPONENT_EXEC_TIMEOUT\", 12)))","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/agent/tools/keenable.py#L85-L121","documentation":"ValueError raised by KeenableSearch.check() at component configuration time. The tool supports mode 'pro' (default, works keyless against /v1/search/public) and 'realtime' (low latency, only on the keyed endpoint /v1/search). Selecting 'realtime' while api_key is empty is rejected immediately instead of failing at request time.","triggerScenarios":"Adding a Keenable search component to an agent canvas, setting mode='realtime', and leaving the api_key parameter blank; or clearing the key later without switching mode back to 'pro'.","commonSituations":"Users wanting lower-latency results without realizing realtime is a keyed feature; copied component JSON from another tenant with the key stripped; API-driven workflow creation that defaults api_key to '' while mode is persisted as 'realtime'.","solutions":["Set the component's api_key parameter to a valid Keenable key (it switches requests to /v1/search with X-API-Key).","Or switch mode back to 'pro' to use the keyless public endpoint.","Re-save/validate the component after changing either field so check() passes before the workflow runs."],"exampleFix":"# before\ncomponent._param.mode = \"realtime\"\ncomponent._param.api_key = \"\"   # ValueError on check()\n\n# after\ncomponent._param.mode = \"realtime\"\ncomponent._param.api_key = os.environ[\"KEENABLE_API_KEY\"]","handlingStrategy":"validation","validationCode":"def keenable_config_ok(mode: str, api_key: str) -> bool:\n    return mode == \"pro\" or bool((api_key or \"\").strip())","typeGuard":null,"tryCatchPattern":"try:\n    component.check()\nexcept ValueError as e:\n    if \"realtime\" in str(e):\n        component._param.mode = \"pro\"  # or supply a key\n    raise","preventionTips":["Always call component.check() when building/saving agent components programmatically.","Treat (mode='realtime', api_key='') as an invalid combination in workflow templates and forms.","Store the Keenable key in env/secret storage and inject it at deploy time."],"tags":["configuration","api-key","keenable","agent-tool"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}