diff --git a/.claude/settings.local.json b/.claude/settings.local.json index d58977f..a3a1353 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -7,7 +7,8 @@ "WebSearch", "Bash(cat:*)", "mcp__firecrawl__firecrawl_search", - "mcp__firecrawl__firecrawl_scrape" + "mcp__firecrawl__firecrawl_scrape", + "Bash(python:*)" ], "deny": [], "ask": [] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4db5fbb --- /dev/null +++ b/.gitignore @@ -0,0 +1,100 @@ +# Environment files +.env +.env.local +.env.*.local + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Virtual environments +.venv/ +.env/ +venv/ +ENV/ +env/ + +# Package managers +.uv_cache/ +.uv-cache/ +.pip-cache/ +pip-log.txt +pip-delete-this-directory.txt + +# Testing +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +htmlcov/ +.nox/ +coverage.xml +*.cover +.hypothesis/ + +# Type checking +.mypy_cache/ +.dmypy.json +dmypy.json +.pyre/ +.pytype/ + +# Linting and formatting +.ruff_cache/ +.flake8_cache/ +.black_cache/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IDE and editors +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs +logs/ +*.log +log/ + +# Temporary files +*.tmp +*.temp +.tmp/ +.temp/ + +# Project specific +repomix-output.xml +*.json.bak +chat.json \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f3865c..2217470 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,37 @@ { "chatgpt.openOnStartup": true, - "python.languageServer": "None", - "python.analysis.typeCheckingMode": "off", "python.defaultInterpreterPath": "./.venv/bin/python", - "python.terminal.activateEnvironment": true + "python.terminal.activateEnvironment": true, + "python.linting.enabled": true, + "python.linting.mypyEnabled": true, + "python.linting.mypyPath": "./.venv/bin/mypy", + "python.linting.pylintEnabled": false, + "python.linting.flake8Enabled": false, + "python.analysis.typeCheckingMode": "basic", + "python.analysis.autoImportCompletions": true, + "python.analysis.stubPath": "./.venv/lib/python3.12/site-packages", + "basedpyright.analysis.typeCheckingMode": "standard", + "basedpyright.analysis.autoSearchPaths": true, + "basedpyright.analysis.autoImportCompletions": true, + "basedpyright.analysis.diagnosticMode": "workspace", + "basedpyright.analysis.stubPath": "./.venv/lib/python3.12/site-packages", + "basedpyright.analysis.extraPaths": [ + "./ingest_pipeline", + "./.venv/lib/python3.12/site-packages" + ], + "pyright.analysis.typeCheckingMode": "standard", + "pyright.analysis.autoSearchPaths": true, + "pyright.analysis.autoImportCompletions": true, + "pyright.analysis.diagnosticMode": "workspace", + "pyright.analysis.stubPath": "./.venv/lib/python3.12/site-packages", + "pyright.analysis.extraPaths": [ + "./ingest_pipeline", + "./.venv/lib/python3.12/site-packages" + ], + "files.exclude": { + "**/__pycache__": true, + "**/.pytest_cache": true, + "**/node_modules": true, + ".mypy_cache": true + } } \ No newline at end of file diff --git a/basedpyright.json b/basedpyright.json index 6441746..62871d4 100644 --- a/basedpyright.json +++ b/basedpyright.json @@ -6,11 +6,26 @@ "**/__pycache__", "**/.pytest_cache", "**/node_modules", - ".venv" + ".venv", + "build", + "dist" ], - "pythonPath": "./.venv/bin/python", + "pythonVersion": "3.12", "venvPath": ".", "venv": ".venv", + "typeCheckingMode": "standard", + "useLibraryCodeForTypes": true, + "stubPath": "./.venv/lib/python3.12/site-packages", + "executionEnvironments": [ + { + "root": ".", + "pythonVersion": "3.12", + "extraPaths": [ + "./ingest_pipeline", + "./.venv/lib/python3.12/site-packages" + ] + } + ], "reportCallInDefaultInitializer": "none", "reportUnknownVariableType": "warning", "reportUnknownMemberType": "warning", @@ -21,9 +36,12 @@ "reportUnannotatedClassAttribute": "warning", "reportMissingTypeStubs": "none", "reportMissingModuleSource": "none", + "reportImportCycles": "none", + "reportAttributeAccessIssue": "warning", "reportAny": "warning", "reportUnusedCallResult": "none", "reportUnnecessaryIsInstance": "none", "reportImplicitOverride": "none", - "reportDeprecated": "warning" + "reportDeprecated": "warning", + "analyzeUnannotatedFunctions": true } diff --git a/chat.json b/chat.json index 64dbb64..a6694f3 100644 --- a/chat.json +++ b/chat.json @@ -1 +1,23482 @@ -{"openapi":"3.1.0","info":{"title":"Open WebUI","version":"0.1.0"},"paths":{"/ollama/":{"get":{"tags":["ollama"],"summary":"Get Status","operationId":"get_status_ollama__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"head":{"tags":["ollama"],"summary":"Get Status","operationId":"get_status_ollama__head","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/ollama/verify":{"post":{"tags":["ollama"],"summary":"Verify Connection","operationId":"verify_connection_ollama_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__ollama__ConnectionVerificationForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/config":{"get":{"tags":["ollama"],"summary":"Get Config","operationId":"get_config_ollama_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/config/update":{"post":{"tags":["ollama"],"summary":"Update Config","operationId":"update_config_ollama_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__ollama__OllamaConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/api/tags/{url_idx}":{"get":{"tags":["ollama"],"summary":"Get Ollama Tags","operationId":"get_ollama_tags_ollama_api_tags__url_idx__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/tags":{"get":{"tags":["ollama"],"summary":"Get Ollama Tags","operationId":"get_ollama_tags_ollama_api_tags_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/ps":{"get":{"tags":["ollama"],"summary":"Get Ollama Loaded Models","description":"List models that are currently loaded into Ollama memory, and which node they are loaded on.","operationId":"get_ollama_loaded_models_ollama_api_ps_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/api/version/{url_idx}":{"get":{"tags":["ollama"],"summary":"Get Ollama Versions","operationId":"get_ollama_versions_ollama_api_version__url_idx__get","parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/version":{"get":{"tags":["ollama"],"summary":"Get Ollama Versions","operationId":"get_ollama_versions_ollama_api_version_get","parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/unload":{"post":{"tags":["ollama"],"summary":"Unload Model","operationId":"unload_model_ollama_api_unload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/api/pull/{url_idx}":{"post":{"tags":["ollama"],"summary":"Pull Model","operationId":"pull_model_ollama_api_pull__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"type":"integer","title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/pull":{"post":{"tags":["ollama"],"summary":"Pull Model","operationId":"pull_model_ollama_api_pull_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/push/{url_idx}":{"delete":{"tags":["ollama"],"summary":"Push Model","operationId":"push_model_ollama_api_push__url_idx__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/push":{"delete":{"tags":["ollama"],"summary":"Push Model","operationId":"push_model_ollama_api_push_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/create/{url_idx}":{"post":{"tags":["ollama"],"summary":"Create Model","operationId":"create_model_ollama_api_create__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"type":"integer","title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/create":{"post":{"tags":["ollama"],"summary":"Create Model","operationId":"create_model_ollama_api_create_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/copy/{url_idx}":{"post":{"tags":["ollama"],"summary":"Copy Model","operationId":"copy_model_ollama_api_copy__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/copy":{"post":{"tags":["ollama"],"summary":"Copy Model","operationId":"copy_model_ollama_api_copy_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CopyModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/delete/{url_idx}":{"delete":{"tags":["ollama"],"summary":"Delete Model","operationId":"delete_model_ollama_api_delete__url_idx__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/delete":{"delete":{"tags":["ollama"],"summary":"Delete Model","operationId":"delete_model_ollama_api_delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/show":{"post":{"tags":["ollama"],"summary":"Show Model Info","operationId":"show_model_info_ollama_api_show_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelNameForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/ollama/api/embed/{url_idx}":{"post":{"tags":["ollama"],"summary":"Embed","operationId":"embed_ollama_api_embed__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateEmbedForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/embed":{"post":{"tags":["ollama"],"summary":"Embed","operationId":"embed_ollama_api_embed_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateEmbedForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/embeddings/{url_idx}":{"post":{"tags":["ollama"],"summary":"Embeddings","operationId":"embeddings_ollama_api_embeddings__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateEmbeddingsForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/embeddings":{"post":{"tags":["ollama"],"summary":"Embeddings","operationId":"embeddings_ollama_api_embeddings_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateEmbeddingsForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/generate/{url_idx}":{"post":{"tags":["ollama"],"summary":"Generate Completion","operationId":"generate_completion_ollama_api_generate__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCompletionForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/generate":{"post":{"tags":["ollama"],"summary":"Generate Completion","operationId":"generate_completion_ollama_api_generate_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCompletionForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/chat/{url_idx}":{"post":{"tags":["ollama"],"summary":"Generate Chat Completion","operationId":"generate_chat_completion_ollama_api_chat__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}},{"name":"bypass_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":false,"title":"Bypass Filter"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/api/chat":{"post":{"tags":["ollama"],"summary":"Generate Chat Completion","operationId":"generate_chat_completion_ollama_api_chat_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}},{"name":"bypass_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":false,"title":"Bypass Filter"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/completions/{url_idx}":{"post":{"tags":["ollama"],"summary":"Generate Openai Completion","operationId":"generate_openai_completion_ollama_v1_completions__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/completions":{"post":{"tags":["ollama"],"summary":"Generate Openai Completion","operationId":"generate_openai_completion_ollama_v1_completions_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/chat/completions/{url_idx}":{"post":{"tags":["ollama"],"summary":"Generate Openai Chat Completion","operationId":"generate_openai_chat_completion_ollama_v1_chat_completions__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/chat/completions":{"post":{"tags":["ollama"],"summary":"Generate Openai Chat Completion","operationId":"generate_openai_chat_completion_ollama_v1_chat_completions_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/models/{url_idx}":{"get":{"tags":["ollama"],"summary":"Get Openai Models","operationId":"get_openai_models_ollama_v1_models__url_idx__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/v1/models":{"get":{"tags":["ollama"],"summary":"Get Openai Models","operationId":"get_openai_models_ollama_v1_models_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/models/download/{url_idx}":{"post":{"tags":["ollama"],"summary":"Download Model","operationId":"download_model_ollama_models_download__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/models/download":{"post":{"tags":["ollama"],"summary":"Download Model","operationId":"download_model_ollama_models_download_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/models/upload/{url_idx}":{"post":{"tags":["ollama"],"summary":"Upload Model","operationId":"upload_model_ollama_models_upload__url_idx__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_model_ollama_models_upload__url_idx__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/ollama/models/upload":{"post":{"tags":["ollama"],"summary":"Upload Model","operationId":"upload_model_ollama_models_upload_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_model_ollama_models_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/openai/config":{"get":{"tags":["openai"],"summary":"Get Config","operationId":"get_config_openai_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/openai/config/update":{"post":{"tags":["openai"],"summary":"Update Config","operationId":"update_config_openai_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__openai__OpenAIConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/openai/audio/speech":{"post":{"tags":["openai"],"summary":"Speech","operationId":"speech_openai_audio_speech_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/openai/models/{url_idx}":{"get":{"tags":["openai"],"summary":"Get Models","operationId":"get_models_openai_models__url_idx__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"path","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/openai/models":{"get":{"tags":["openai"],"summary":"Get Models","operationId":"get_models_openai_models_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"url_idx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Url Idx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/openai/verify":{"post":{"tags":["openai"],"summary":"Verify Connection","operationId":"verify_connection_openai_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__openai__ConnectionVerificationForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/openai/chat/completions":{"post":{"tags":["openai"],"summary":"Generate Chat Completion","operationId":"generate_chat_completion_openai_chat_completions_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"bypass_filter","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"default":false,"title":"Bypass Filter"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/openai/{path}":{"delete":{"tags":["openai"],"summary":"Proxy","description":"Deprecated: proxy all requests to OpenAI API","operationId":"proxy_openai__path__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["openai"],"summary":"Proxy","description":"Deprecated: proxy all requests to OpenAI API","operationId":"proxy_openai__path__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["openai"],"summary":"Proxy","description":"Deprecated: proxy all requests to OpenAI API","operationId":"proxy_openai__path__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["openai"],"summary":"Proxy","description":"Deprecated: proxy all requests to OpenAI API","operationId":"proxy_openai__path__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/pipelines/list":{"get":{"tags":["pipelines"],"summary":"Get Pipelines List","operationId":"get_pipelines_list_api_v1_pipelines_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/pipelines/upload":{"post":{"tags":["pipelines"],"summary":"Upload Pipeline","operationId":"upload_pipeline_api_v1_pipelines_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_pipeline_api_v1_pipelines_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/pipelines/add":{"post":{"tags":["pipelines"],"summary":"Add Pipeline","operationId":"add_pipeline_api_v1_pipelines_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddPipelineForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/pipelines/delete":{"delete":{"tags":["pipelines"],"summary":"Delete Pipeline","operationId":"delete_pipeline_api_v1_pipelines_delete_delete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeletePipelineForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/pipelines/":{"get":{"tags":["pipelines"],"summary":"Get Pipelines","operationId":"get_pipelines_api_v1_pipelines__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"urlIdx","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Urlidx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/pipelines/{pipeline_id}/valves":{"get":{"tags":["pipelines"],"summary":"Get Pipeline Valves","operationId":"get_pipeline_valves_api_v1_pipelines__pipeline_id__valves_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"urlIdx","in":"query","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Urlidx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/pipelines/{pipeline_id}/valves/spec":{"get":{"tags":["pipelines"],"summary":"Get Pipeline Valves Spec","operationId":"get_pipeline_valves_spec_api_v1_pipelines__pipeline_id__valves_spec_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"urlIdx","in":"query","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Urlidx"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/pipelines/{pipeline_id}/valves/update":{"post":{"tags":["pipelines"],"summary":"Update Pipeline Valves","operationId":"update_pipeline_valves_api_v1_pipelines__pipeline_id__valves_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"pipeline_id","in":"path","required":true,"schema":{"type":"string","title":"Pipeline Id"}},{"name":"urlIdx","in":"query","required":true,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Urlidx"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/config":{"get":{"tags":["tasks"],"summary":"Get Task Config","operationId":"get_task_config_api_v1_tasks_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/config/update":{"post":{"tags":["tasks"],"summary":"Update Task Config","operationId":"update_task_config_api_v1_tasks_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/title/completions":{"post":{"tags":["tasks"],"summary":"Generate Title","operationId":"generate_title_api_v1_tasks_title_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/follow_up/completions":{"post":{"tags":["tasks"],"summary":"Generate Follow Ups","operationId":"generate_follow_ups_api_v1_tasks_follow_up_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/tags/completions":{"post":{"tags":["tasks"],"summary":"Generate Chat Tags","operationId":"generate_chat_tags_api_v1_tasks_tags_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/image_prompt/completions":{"post":{"tags":["tasks"],"summary":"Generate Image Prompt","operationId":"generate_image_prompt_api_v1_tasks_image_prompt_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/queries/completions":{"post":{"tags":["tasks"],"summary":"Generate Queries","operationId":"generate_queries_api_v1_tasks_queries_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/auto/completions":{"post":{"tags":["tasks"],"summary":"Generate Autocompletion","operationId":"generate_autocompletion_api_v1_tasks_auto_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/emoji/completions":{"post":{"tags":["tasks"],"summary":"Generate Emoji","operationId":"generate_emoji_api_v1_tasks_emoji_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tasks/moa/completions":{"post":{"tags":["tasks"],"summary":"Generate Moa Response","operationId":"generate_moa_response_api_v1_tasks_moa_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/config":{"get":{"tags":["images"],"summary":"Get Config","operationId":"get_config_api_v1_images_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/config/update":{"post":{"tags":["images"],"summary":"Update Config","operationId":"update_config_api_v1_images_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__images__ConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/config/url/verify":{"get":{"tags":["images"],"summary":"Verify Url","operationId":"verify_url_api_v1_images_config_url_verify_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/image/config":{"get":{"tags":["images"],"summary":"Get Image Config","operationId":"get_image_config_api_v1_images_image_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/image/config/update":{"post":{"tags":["images"],"summary":"Update Image Config","operationId":"update_image_config_api_v1_images_image_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/models":{"get":{"tags":["images"],"summary":"Get Models","operationId":"get_models_api_v1_images_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/images/generations":{"post":{"tags":["images"],"summary":"Image Generations","operationId":"image_generations_api_v1_images_generations_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/config":{"get":{"tags":["audio"],"summary":"Get Audio Config","operationId":"get_audio_config_api_v1_audio_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/config/update":{"post":{"tags":["audio"],"summary":"Update Audio Config","operationId":"update_audio_config_api_v1_audio_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AudioConfigUpdateForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/speech":{"post":{"tags":["audio"],"summary":"Speech","operationId":"speech_api_v1_audio_speech_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/transcriptions":{"post":{"tags":["audio"],"summary":"Transcription","operationId":"transcription_api_v1_audio_transcriptions_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_transcription_api_v1_audio_transcriptions_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/models":{"get":{"tags":["audio"],"summary":"Get Models","operationId":"get_models_api_v1_audio_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/audio/voices":{"get":{"tags":["audio"],"summary":"Get Voices","operationId":"get_voices_api_v1_audio_voices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/":{"get":{"tags":["retrieval"],"summary":"Get Status","operationId":"get_status_api_v1_retrieval__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/retrieval/embedding":{"get":{"tags":["retrieval"],"summary":"Get Embedding Config","operationId":"get_embedding_config_api_v1_retrieval_embedding_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/embedding/update":{"post":{"tags":["retrieval"],"summary":"Update Embedding Config","operationId":"update_embedding_config_api_v1_retrieval_embedding_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingModelUpdateForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/config":{"get":{"tags":["retrieval"],"summary":"Get Rag Config","operationId":"get_rag_config_api_v1_retrieval_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/config/update":{"post":{"tags":["retrieval"],"summary":"Update Rag Config","operationId":"update_rag_config_api_v1_retrieval_config_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__retrieval__ConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/process/file":{"post":{"tags":["retrieval"],"summary":"Process File","operationId":"process_file_api_v1_retrieval_process_file_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessFileForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/process/text":{"post":{"tags":["retrieval"],"summary":"Process Text","operationId":"process_text_api_v1_retrieval_process_text_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessTextForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/process/youtube":{"post":{"tags":["retrieval"],"summary":"Process Youtube Video","operationId":"process_youtube_video_api_v1_retrieval_process_youtube_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessUrlForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/process/web":{"post":{"tags":["retrieval"],"summary":"Process Web","operationId":"process_web_api_v1_retrieval_process_web_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProcessUrlForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/process/web/search":{"post":{"tags":["retrieval"],"summary":"Process Web Search","operationId":"process_web_search_api_v1_retrieval_process_web_search_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/query/doc":{"post":{"tags":["retrieval"],"summary":"Query Doc Handler","operationId":"query_doc_handler_api_v1_retrieval_query_doc_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryDocForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/query/collection":{"post":{"tags":["retrieval"],"summary":"Query Collection Handler","operationId":"query_collection_handler_api_v1_retrieval_query_collection_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryCollectionsForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/delete":{"post":{"tags":["retrieval"],"summary":"Delete Entries From Collection","operationId":"delete_entries_from_collection_api_v1_retrieval_delete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/reset/db":{"post":{"tags":["retrieval"],"summary":"Reset Vector Db","operationId":"reset_vector_db_api_v1_retrieval_reset_db_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/reset/uploads":{"post":{"tags":["retrieval"],"summary":"Reset Upload Dir","operationId":"reset_upload_dir_api_v1_retrieval_reset_uploads_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Reset Upload Dir Api V1 Retrieval Reset Uploads Post"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/retrieval/ef/{text}":{"get":{"tags":["retrieval"],"summary":"Get Embeddings","operationId":"get_embeddings_api_v1_retrieval_ef__text__get","parameters":[{"name":"text","in":"path","required":true,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/retrieval/process/files/batch":{"post":{"tags":["retrieval"],"summary":"Process Files Batch","description":"Process a batch of files and save them to the vector database.","operationId":"process_files_batch_api_v1_retrieval_process_files_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProcessFilesForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchProcessFilesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/import":{"post":{"tags":["configs"],"summary":"Import Config","operationId":"import_config_api_v1_configs_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Import Config Api V1 Configs Import Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/export":{"get":{"tags":["configs"],"summary":"Export Config","operationId":"export_config_api_v1_configs_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Export Config Api V1 Configs Export Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/connections":{"get":{"tags":["configs"],"summary":"Get Connections Config","operationId":"get_connections_config_api_v1_configs_connections_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionsConfigForm"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["configs"],"summary":"Set Connections Config","operationId":"set_connections_config_api_v1_configs_connections_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionsConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionsConfigForm"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/tool_servers":{"get":{"tags":["configs"],"summary":"Get Tool Servers Config","operationId":"get_tool_servers_config_api_v1_configs_tool_servers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolServersConfigForm"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["configs"],"summary":"Set Tool Servers Config","operationId":"set_tool_servers_config_api_v1_configs_tool_servers_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolServersConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolServersConfigForm"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/tool_servers/verify":{"post":{"tags":["configs"],"summary":"Verify Tool Servers Config","description":"Verify the connection to the tool server.","operationId":"verify_tool_servers_config_api_v1_configs_tool_servers_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolServerConnection"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/code_execution":{"get":{"tags":["configs"],"summary":"Get Code Execution Config","operationId":"get_code_execution_config_api_v1_configs_code_execution_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeInterpreterConfigForm"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["configs"],"summary":"Set Code Execution Config","operationId":"set_code_execution_config_api_v1_configs_code_execution_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeInterpreterConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeInterpreterConfigForm"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/models":{"get":{"tags":["configs"],"summary":"Get Models Config","operationId":"get_models_config_api_v1_configs_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsConfigForm"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["configs"],"summary":"Set Models Config","operationId":"set_models_config_api_v1_configs_models_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelsConfigForm"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/suggestions":{"post":{"tags":["configs"],"summary":"Set Default Suggestions","operationId":"set_default_suggestions_api_v1_configs_suggestions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetDefaultSuggestionsForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PromptSuggestion"},"type":"array","title":"Response Set Default Suggestions Api V1 Configs Suggestions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/configs/banners":{"get":{"tags":["configs"],"summary":"Get Banners","operationId":"get_banners_api_v1_configs_banners_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BannerModel"},"type":"array","title":"Response Get Banners Api V1 Configs Banners Get"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["configs"],"summary":"Set Banners","operationId":"set_banners_api_v1_configs_banners_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetBannersForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/BannerModel"},"type":"array","title":"Response Set Banners Api V1 Configs Banners Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/":{"get":{"tags":["auths"],"summary":"Get Session User","operationId":"get_session_user_api_v1_auths__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionUserInfoResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/update/profile":{"post":{"tags":["auths"],"summary":"Update Profile","operationId":"update_profile_api_v1_auths_update_profile_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProfileForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__models__auths__UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/update/password":{"post":{"tags":["auths"],"summary":"Update Password","operationId":"update_password_api_v1_auths_update_password_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePasswordForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Update Password Api V1 Auths Update Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/ldap":{"post":{"tags":["auths"],"summary":"Ldap Auth","operationId":"ldap_auth_api_v1_auths_ldap_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LdapForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auths/signin":{"post":{"tags":["auths"],"summary":"Signin","operationId":"signin_api_v1_auths_signin_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SigninForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auths/signup":{"post":{"tags":["auths"],"summary":"Signup","operationId":"signup_api_v1_auths_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SessionUserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auths/signout":{"get":{"tags":["auths"],"summary":"Signout","operationId":"signout_api_v1_auths_signout_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auths/add":{"post":{"tags":["auths"],"summary":"Add User","operationId":"add_user_api_v1_auths_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddUserForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SigninResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/admin/details":{"get":{"tags":["auths"],"summary":"Get Admin Details","operationId":"get_admin_details_api_v1_auths_admin_details_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/admin/config":{"get":{"tags":["auths"],"summary":"Get Admin Config","operationId":"get_admin_config_api_v1_auths_admin_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["auths"],"summary":"Update Admin Config","operationId":"update_admin_config_api_v1_auths_admin_config_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/admin/config/ldap/server":{"get":{"tags":["auths"],"summary":"Get Ldap Server","operationId":"get_ldap_server_api_v1_auths_admin_config_ldap_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LdapServerConfig"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["auths"],"summary":"Update Ldap Server","operationId":"update_ldap_server_api_v1_auths_admin_config_ldap_server_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LdapServerConfig"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/admin/config/ldap":{"get":{"tags":["auths"],"summary":"Get Ldap Config","operationId":"get_ldap_config_api_v1_auths_admin_config_ldap_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["auths"],"summary":"Update Ldap Config","operationId":"update_ldap_config_api_v1_auths_admin_config_ldap_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LdapConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/auths/api_key":{"get":{"tags":["auths"],"summary":"Get Api Key","operationId":"get_api_key_api_v1_auths_api_key_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["auths"],"summary":"Generate Api Key","operationId":"generate_api_key_api_v1_auths_api_key_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKey"}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"tags":["auths"],"summary":"Delete Api Key","operationId":"delete_api_key_api_v1_auths_api_key_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Api Key Api V1 Auths Api Key Delete"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/active":{"get":{"tags":["users"],"summary":"Get Active Users","description":"Get a list of active users.","operationId":"get_active_users_api_v1_users_active_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/":{"get":{"tags":["users"],"summary":"Get Users","operationId":"get_users_api_v1_users__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"}},{"name":"order_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order By"}},{"name":"direction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Direction"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":1,"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/all":{"get":{"tags":["users"],"summary":"Get All Users","operationId":"get_all_users_api_v1_users_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfoListResponse"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/groups":{"get":{"tags":["users"],"summary":"Get User Groups","operationId":"get_user_groups_api_v1_users_groups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/permissions":{"get":{"tags":["users"],"summary":"Get User Permissisions","operationId":"get_user_permissisions_api_v1_users_permissions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/default/permissions":{"get":{"tags":["users"],"summary":"Get Default User Permissions","operationId":"get_default_user_permissions_api_v1_users_default_permissions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPermissions"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["users"],"summary":"Update Default User Permissions","operationId":"update_default_user_permissions_api_v1_users_default_permissions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPermissions"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/user/settings":{"get":{"tags":["users"],"summary":"Get User Settings By Session User","operationId":"get_user_settings_by_session_user_api_v1_users_user_settings_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/UserSettings"},{"type":"null"}],"title":"Response Get User Settings By Session User Api V1 Users User Settings Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/user/settings/update":{"post":{"tags":["users"],"summary":"Update User Settings By Session User","operationId":"update_user_settings_by_session_user_api_v1_users_user_settings_update_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSettings"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/user/info":{"get":{"tags":["users"],"summary":"Get User Info By Session User","operationId":"get_user_info_by_session_user_api_v1_users_user_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response Get User Info By Session User Api V1 Users User Info Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/user/info/update":{"post":{"tags":["users"],"summary":"Update User Info By Session User","operationId":"update_user_info_by_session_user_api_v1_users_user_info_update_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response Update User Info By Session User Api V1 Users User Info Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/users/{user_id}":{"get":{"tags":["users"],"summary":"Get User By Id","operationId":"get_user_by_id_api_v1_users__user_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__users__UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["users"],"summary":"Delete User By Id","operationId":"delete_user_by_id_api_v1_users__user_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete User By Id Api V1 Users User Id Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/oauth/sessions":{"get":{"tags":["users"],"summary":"Get User Oauth Sessions By Id","operationId":"get_user_oauth_sessions_by_id_api_v1_users__user_id__oauth_sessions_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get User Oauth Sessions By Id Api V1 Users User Id Oauth Sessions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/profile/image":{"get":{"tags":["users"],"summary":"Get User Profile Image By Id","operationId":"get_user_profile_image_by_id_api_v1_users__user_id__profile_image_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/active":{"get":{"tags":["users"],"summary":"Get User Active Status By Id","operationId":"get_user_active_status_by_id_api_v1_users__user_id__active_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get User Active Status By Id Api V1 Users User Id Active Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/update":{"post":{"tags":["users"],"summary":"Update User By Id","operationId":"update_user_by_id_api_v1_users__user_id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/UserModel"},{"type":"null"}],"title":"Response Update User By Id Api V1 Users User Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/{user_id}/groups":{"get":{"tags":["users"],"summary":"Get User Groups By Id","operationId":"get_user_groups_by_id_api_v1_users__user_id__groups_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/":{"get":{"tags":["channels"],"summary":"Get Channels","operationId":"get_channels_api_v1_channels__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChannelModel"},"type":"array","title":"Response Get Channels Api V1 Channels Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/channels/list":{"get":{"tags":["channels"],"summary":"Get All Channels","operationId":"get_all_channels_api_v1_channels_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChannelModel"},"type":"array","title":"Response Get All Channels Api V1 Channels List Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/channels/create":{"post":{"tags":["channels"],"summary":"Create New Channel","operationId":"create_new_channel_api_v1_channels_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChannelModel"},{"type":"null"}],"title":"Response Create New Channel Api V1 Channels Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/channels/{id}":{"get":{"tags":["channels"],"summary":"Get Channel By Id","operationId":"get_channel_by_id_api_v1_channels__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChannelModel"},{"type":"null"}],"title":"Response Get Channel By Id Api V1 Channels Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/update":{"post":{"tags":["channels"],"summary":"Update Channel By Id","operationId":"update_channel_by_id_api_v1_channels__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChannelModel"},{"type":"null"}],"title":"Response Update Channel By Id Api V1 Channels Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/delete":{"delete":{"tags":["channels"],"summary":"Delete Channel By Id","operationId":"delete_channel_by_id_api_v1_channels__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Channel By Id Api V1 Channels Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages":{"get":{"tags":["channels"],"summary":"Get Channel Messages","operationId":"get_channel_messages_api_v1_channels__id__messages_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageUserResponse"},"title":"Response Get Channel Messages Api V1 Channels Id Messages Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/post":{"post":{"tags":["channels"],"summary":"Post New Message","operationId":"post_new_message_api_v1_channels__id__messages_post_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__models__messages__MessageForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MessageModel"},{"type":"null"}],"title":"Response Post New Message Api V1 Channels Id Messages Post Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}":{"get":{"tags":["channels"],"summary":"Get Channel Message","operationId":"get_channel_message_api_v1_channels__id__messages__message_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MessageUserResponse"},{"type":"null"}],"title":"Response Get Channel Message Api V1 Channels Id Messages Message Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}/thread":{"get":{"tags":["channels"],"summary":"Get Channel Thread Messages","operationId":"get_channel_thread_messages_api_v1_channels__id__messages__message_id__thread_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}},{"name":"skip","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Skip"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/MessageUserResponse"},"title":"Response Get Channel Thread Messages Api V1 Channels Id Messages Message Id Thread Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}/update":{"post":{"tags":["channels"],"summary":"Update Message By Id","operationId":"update_message_by_id_api_v1_channels__id__messages__message_id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__models__messages__MessageForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MessageModel"},{"type":"null"}],"title":"Response Update Message By Id Api V1 Channels Id Messages Message Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}/reactions/add":{"post":{"tags":["channels"],"summary":"Add Reaction To Message","operationId":"add_reaction_to_message_api_v1_channels__id__messages__message_id__reactions_add_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Add Reaction To Message Api V1 Channels Id Messages Message Id Reactions Add Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}/reactions/remove":{"post":{"tags":["channels"],"summary":"Remove Reaction By Id And User Id And Name","operationId":"remove_reaction_by_id_and_user_id_and_name_api_v1_channels__id__messages__message_id__reactions_remove_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Remove Reaction By Id And User Id And Name Api V1 Channels Id Messages Message Id Reactions Remove Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/channels/{id}/messages/{message_id}/delete":{"delete":{"tags":["channels"],"summary":"Delete Message By Id","operationId":"delete_message_by_id_api_v1_channels__id__messages__message_id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Message By Id Api V1 Channels Id Messages Message Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/list":{"get":{"tags":["chats"],"summary":"Get Session User Chat List","operationId":"get_session_user_chat_list_api_v1_chats_list_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"title":"Response Get Session User Chat List Api V1 Chats List Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/":{"get":{"tags":["chats"],"summary":"Get Session User Chat List","operationId":"get_session_user_chat_list_api_v1_chats__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"title":"Response Get Session User Chat List Api V1 Chats Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Delete All User Chats","operationId":"delete_all_user_chats_api_v1_chats__delete","security":[{"HTTPBearer":[]}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete All User Chats Api V1 Chats Delete"}}}}}}},"/api/v1/chats/list/user/{user_id}":{"get":{"tags":["chats"],"summary":"Get User Chat List By User Id","operationId":"get_user_chat_list_by_user_id_api_v1_chats_list_user__user_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"}},{"name":"order_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order By"}},{"name":"direction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Direction"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"title":"Response Get User Chat List By User Id Api V1 Chats List User User Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/new":{"post":{"tags":["chats"],"summary":"Create New Chat","operationId":"create_new_chat_api_v1_chats_new_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Create New Chat Api V1 Chats New Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/import":{"post":{"tags":["chats"],"summary":"Import Chat","operationId":"import_chat_api_v1_chats_import_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatImportForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Import Chat Api V1 Chats Import Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/search":{"get":{"tags":["chats"],"summary":"Search User Chats","operationId":"search_user_chats_api_v1_chats_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"text","in":"query","required":true,"schema":{"type":"string","title":"Text"}},{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"title":"Response Search User Chats Api V1 Chats Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/folder/{folder_id}":{"get":{"tags":["chats"],"summary":"Get Chats By Folder Id","operationId":"get_chats_by_folder_id_api_v1_chats_folder__folder_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"folder_id","in":"path","required":true,"schema":{"type":"string","title":"Folder Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatResponse"},"title":"Response Get Chats By Folder Id Api V1 Chats Folder Folder Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/pinned":{"get":{"tags":["chats"],"summary":"Get User Pinned Chats","operationId":"get_user_pinned_chats_api_v1_chats_pinned_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"type":"array","title":"Response Get User Pinned Chats Api V1 Chats Pinned Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/all":{"get":{"tags":["chats"],"summary":"Get User Chats","operationId":"get_user_chats_api_v1_chats_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChatResponse"},"type":"array","title":"Response Get User Chats Api V1 Chats All Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/all/archived":{"get":{"tags":["chats"],"summary":"Get User Archived Chats","operationId":"get_user_archived_chats_api_v1_chats_all_archived_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChatResponse"},"type":"array","title":"Response Get User Archived Chats Api V1 Chats All Archived Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/all/tags":{"get":{"tags":["chats"],"summary":"Get All User Tags","operationId":"get_all_user_tags_api_v1_chats_all_tags_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/TagModel"},"type":"array","title":"Response Get All User Tags Api V1 Chats All Tags Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/all/db":{"get":{"tags":["chats"],"summary":"Get All User Chats In Db","operationId":"get_all_user_chats_in_db_api_v1_chats_all_db_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChatResponse"},"type":"array","title":"Response Get All User Chats In Db Api V1 Chats All Db Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/archived":{"get":{"tags":["chats"],"summary":"Get Archived Session User Chat List","operationId":"get_archived_session_user_chat_list_api_v1_chats_archived_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"page","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Page"}},{"name":"query","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"}},{"name":"order_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Order By"}},{"name":"direction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Direction"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"title":"Response Get Archived Session User Chat List Api V1 Chats Archived Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/archive/all":{"post":{"tags":["chats"],"summary":"Archive All Chats","operationId":"archive_all_chats_api_v1_chats_archive_all_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Archive All Chats Api V1 Chats Archive All Post"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/share/{share_id}":{"get":{"tags":["chats"],"summary":"Get Shared Chat By Id","operationId":"get_shared_chat_by_id_api_v1_chats_share__share_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"share_id","in":"path","required":true,"schema":{"type":"string","title":"Share Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Get Shared Chat By Id Api V1 Chats Share Share Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/tags":{"post":{"tags":["chats"],"summary":"Get User Chat List By Tag Name","operationId":"get_user_chat_list_by_tag_name_api_v1_chats_tags_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagFilterForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ChatTitleIdResponse"},"type":"array","title":"Response Get User Chat List By Tag Name Api V1 Chats Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chats/{id}":{"get":{"tags":["chats"],"summary":"Get Chat By Id","operationId":"get_chat_by_id_api_v1_chats__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Get Chat By Id Api V1 Chats Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["chats"],"summary":"Update Chat By Id","operationId":"update_chat_by_id_api_v1_chats__id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Update Chat By Id Api V1 Chats Id Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Delete Chat By Id","operationId":"delete_chat_by_id_api_v1_chats__id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Chat By Id Api V1 Chats Id Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/messages/{message_id}":{"post":{"tags":["chats"],"summary":"Update Chat Message By Id","operationId":"update_chat_message_by_id_api_v1_chats__id__messages__message_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/open_webui__routers__chats__MessageForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Update Chat Message By Id Api V1 Chats Id Messages Message Id Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/messages/{message_id}/event":{"post":{"tags":["chats"],"summary":"Send Chat Message Event By Id","operationId":"send_chat_message_event_by_id_api_v1_chats__id__messages__message_id__event_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"message_id","in":"path","required":true,"schema":{"type":"string","title":"Message Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Send Chat Message Event By Id Api V1 Chats Id Messages Message Id Event Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/pinned":{"get":{"tags":["chats"],"summary":"Get Pinned Status By Id","operationId":"get_pinned_status_by_id_api_v1_chats__id__pinned_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Get Pinned Status By Id Api V1 Chats Id Pinned Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/pin":{"post":{"tags":["chats"],"summary":"Pin Chat By Id","operationId":"pin_chat_by_id_api_v1_chats__id__pin_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Pin Chat By Id Api V1 Chats Id Pin Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/clone":{"post":{"tags":["chats"],"summary":"Clone Chat By Id","operationId":"clone_chat_by_id_api_v1_chats__id__clone_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CloneForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Clone Chat By Id Api V1 Chats Id Clone Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/clone/shared":{"post":{"tags":["chats"],"summary":"Clone Shared Chat By Id","operationId":"clone_shared_chat_by_id_api_v1_chats__id__clone_shared_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Clone Shared Chat By Id Api V1 Chats Id Clone Shared Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/archive":{"post":{"tags":["chats"],"summary":"Archive Chat By Id","operationId":"archive_chat_by_id_api_v1_chats__id__archive_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Archive Chat By Id Api V1 Chats Id Archive Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/share":{"post":{"tags":["chats"],"summary":"Share Chat By Id","operationId":"share_chat_by_id_api_v1_chats__id__share_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Share Chat By Id Api V1 Chats Id Share Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Delete Shared Chat By Id","operationId":"delete_shared_chat_by_id_api_v1_chats__id__share_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Delete Shared Chat By Id Api V1 Chats Id Share Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/folder":{"post":{"tags":["chats"],"summary":"Update Chat Folder Id By Id","operationId":"update_chat_folder_id_by_id_api_v1_chats__id__folder_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatFolderIdForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ChatResponse"},{"type":"null"}],"title":"Response Update Chat Folder Id By Id Api V1 Chats Id Folder Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/tags":{"get":{"tags":["chats"],"summary":"Get Chat Tags By Id","operationId":"get_chat_tags_by_id_api_v1_chats__id__tags_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagModel"},"title":"Response Get Chat Tags By Id Api V1 Chats Id Tags Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["chats"],"summary":"Add Tag By Id And Tag Name","operationId":"add_tag_by_id_and_tag_name_api_v1_chats__id__tags_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagModel"},"title":"Response Add Tag By Id And Tag Name Api V1 Chats Id Tags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["chats"],"summary":"Delete Tag By Id And Tag Name","operationId":"delete_tag_by_id_and_tag_name_api_v1_chats__id__tags_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TagForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TagModel"},"title":"Response Delete Tag By Id And Tag Name Api V1 Chats Id Tags Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/chats/{id}/tags/all":{"delete":{"tags":["chats"],"summary":"Delete All Tags By Id","operationId":"delete_all_tags_by_id_api_v1_chats__id__tags_all_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Response Delete All Tags By Id Api V1 Chats Id Tags All Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notes/":{"get":{"tags":["notes"],"summary":"Get Notes","operationId":"get_notes_api_v1_notes__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/NoteUserResponse"},"type":"array","title":"Response Get Notes Api V1 Notes Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/notes/list":{"get":{"tags":["notes"],"summary":"Get Note List","operationId":"get_note_list_api_v1_notes_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/NoteTitleIdResponse"},"type":"array","title":"Response Get Note List Api V1 Notes List Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/notes/create":{"post":{"tags":["notes"],"summary":"Create New Note","operationId":"create_new_note_api_v1_notes_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/NoteModel"},{"type":"null"}],"title":"Response Create New Note Api V1 Notes Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/notes/{id}":{"get":{"tags":["notes"],"summary":"Get Note By Id","operationId":"get_note_by_id_api_v1_notes__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/NoteModel"},{"type":"null"}],"title":"Response Get Note By Id Api V1 Notes Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notes/{id}/update":{"post":{"tags":["notes"],"summary":"Update Note By Id","operationId":"update_note_by_id_api_v1_notes__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoteForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/NoteModel"},{"type":"null"}],"title":"Response Update Note By Id Api V1 Notes Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/notes/{id}/delete":{"delete":{"tags":["notes"],"summary":"Delete Note By Id","operationId":"delete_note_by_id_api_v1_notes__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Note By Id Api V1 Notes Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/":{"get":{"tags":["models"],"summary":"Get Models","operationId":"get_models_api_v1_models__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ModelUserResponse"},"title":"Response Get Models Api V1 Models Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/base":{"get":{"tags":["models"],"summary":"Get Base Models","operationId":"get_base_models_api_v1_models_base_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ModelResponse"},"type":"array","title":"Response Get Base Models Api V1 Models Base Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/models/create":{"post":{"tags":["models"],"summary":"Create New Model","operationId":"create_new_model_api_v1_models_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ModelModel"},{"type":"null"}],"title":"Response Create New Model Api V1 Models Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/models/export":{"get":{"tags":["models"],"summary":"Export Models","operationId":"export_models_api_v1_models_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ModelModel"},"type":"array","title":"Response Export Models Api V1 Models Export Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/models/sync":{"post":{"tags":["models"],"summary":"Sync Models","operationId":"sync_models_api_v1_models_sync_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncModelsForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ModelModel"},"type":"array","title":"Response Sync Models Api V1 Models Sync Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/models/model":{"get":{"tags":["models"],"summary":"Get Model By Id","operationId":"get_model_by_id_api_v1_models_model_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ModelResponse"},{"type":"null"}],"title":"Response Get Model By Id Api V1 Models Model Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/model/toggle":{"post":{"tags":["models"],"summary":"Toggle Model By Id","operationId":"toggle_model_by_id_api_v1_models_model_toggle_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ModelResponse"},{"type":"null"}],"title":"Response Toggle Model By Id Api V1 Models Model Toggle Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/model/update":{"post":{"tags":["models"],"summary":"Update Model By Id","operationId":"update_model_by_id_api_v1_models_model_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ModelModel"},{"type":"null"}],"title":"Response Update Model By Id Api V1 Models Model Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/model/delete":{"delete":{"tags":["models"],"summary":"Delete Model By Id","operationId":"delete_model_by_id_api_v1_models_model_delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"query","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Model By Id Api V1 Models Model Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/models/delete/all":{"delete":{"tags":["models"],"summary":"Delete All Models","operationId":"delete_all_models_api_v1_models_delete_all_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete All Models Api V1 Models Delete All Delete"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/knowledge/":{"get":{"tags":["knowledge"],"summary":"Get Knowledge","operationId":"get_knowledge_api_v1_knowledge__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KnowledgeUserResponse"},"type":"array","title":"Response Get Knowledge Api V1 Knowledge Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/knowledge/list":{"get":{"tags":["knowledge"],"summary":"Get Knowledge List","operationId":"get_knowledge_list_api_v1_knowledge_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KnowledgeUserResponse"},"type":"array","title":"Response Get Knowledge List Api V1 Knowledge List Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/knowledge/create":{"post":{"tags":["knowledge"],"summary":"Create New Knowledge","operationId":"create_new_knowledge_api_v1_knowledge_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeResponse"},{"type":"null"}],"title":"Response Create New Knowledge Api V1 Knowledge Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/knowledge/reindex":{"post":{"tags":["knowledge"],"summary":"Reindex Knowledge Files","operationId":"reindex_knowledge_files_api_v1_knowledge_reindex_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Reindex Knowledge Files Api V1 Knowledge Reindex Post"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/knowledge/{id}":{"get":{"tags":["knowledge"],"summary":"Get Knowledge By Id","operationId":"get_knowledge_by_id_api_v1_knowledge__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Get Knowledge By Id Api V1 Knowledge Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/update":{"post":{"tags":["knowledge"],"summary":"Update Knowledge By Id","operationId":"update_knowledge_by_id_api_v1_knowledge__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Update Knowledge By Id Api V1 Knowledge Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/file/add":{"post":{"tags":["knowledge"],"summary":"Add File To Knowledge By Id","operationId":"add_file_to_knowledge_by_id_api_v1_knowledge__id__file_add_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeFileIdForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Add File To Knowledge By Id Api V1 Knowledge Id File Add Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/file/update":{"post":{"tags":["knowledge"],"summary":"Update File From Knowledge By Id","operationId":"update_file_from_knowledge_by_id_api_v1_knowledge__id__file_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeFileIdForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Update File From Knowledge By Id Api V1 Knowledge Id File Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/file/remove":{"post":{"tags":["knowledge"],"summary":"Remove File From Knowledge By Id","operationId":"remove_file_from_knowledge_by_id_api_v1_knowledge__id__file_remove_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"delete_file","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Delete File"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeFileIdForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Remove File From Knowledge By Id Api V1 Knowledge Id File Remove Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/delete":{"delete":{"tags":["knowledge"],"summary":"Delete Knowledge By Id","operationId":"delete_knowledge_by_id_api_v1_knowledge__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Knowledge By Id Api V1 Knowledge Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/reset":{"post":{"tags":["knowledge"],"summary":"Reset Knowledge By Id","operationId":"reset_knowledge_by_id_api_v1_knowledge__id__reset_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeResponse"},{"type":"null"}],"title":"Response Reset Knowledge By Id Api V1 Knowledge Id Reset Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/knowledge/{id}/files/batch/add":{"post":{"tags":["knowledge"],"summary":"Add Files To Knowledge Batch","description":"Add multiple files to a knowledge base","operationId":"add_files_to_knowledge_batch_api_v1_knowledge__id__files_batch_add_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeFileIdForm"},"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/KnowledgeFilesResponse"},{"type":"null"}],"title":"Response Add Files To Knowledge Batch Api V1 Knowledge Id Files Batch Add Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prompts/":{"get":{"tags":["prompts"],"summary":"Get Prompts","operationId":"get_prompts_api_v1_prompts__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PromptModel"},"type":"array","title":"Response Get Prompts Api V1 Prompts Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/prompts/list":{"get":{"tags":["prompts"],"summary":"Get Prompt List","operationId":"get_prompt_list_api_v1_prompts_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PromptUserResponse"},"type":"array","title":"Response Get Prompt List Api V1 Prompts List Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/prompts/create":{"post":{"tags":["prompts"],"summary":"Create New Prompt","operationId":"create_new_prompt_api_v1_prompts_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PromptModel"},{"type":"null"}],"title":"Response Create New Prompt Api V1 Prompts Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/prompts/command/{command}":{"get":{"tags":["prompts"],"summary":"Get Prompt By Command","operationId":"get_prompt_by_command_api_v1_prompts_command__command__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"command","in":"path","required":true,"schema":{"type":"string","title":"Command"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PromptModel"},{"type":"null"}],"title":"Response Get Prompt By Command Api V1 Prompts Command Command Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prompts/command/{command}/update":{"post":{"tags":["prompts"],"summary":"Update Prompt By Command","operationId":"update_prompt_by_command_api_v1_prompts_command__command__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"command","in":"path","required":true,"schema":{"type":"string","title":"Command"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromptForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/PromptModel"},{"type":"null"}],"title":"Response Update Prompt By Command Api V1 Prompts Command Command Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/prompts/command/{command}/delete":{"delete":{"tags":["prompts"],"summary":"Delete Prompt By Command","operationId":"delete_prompt_by_command_api_v1_prompts_command__command__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"command","in":"path","required":true,"schema":{"type":"string","title":"Command"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Prompt By Command Api V1 Prompts Command Command Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/":{"get":{"tags":["tools"],"summary":"Get Tools","operationId":"get_tools_api_v1_tools__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolUserResponse"},"type":"array","title":"Response Get Tools Api V1 Tools Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tools/list":{"get":{"tags":["tools"],"summary":"Get Tool List","operationId":"get_tool_list_api_v1_tools_list_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolUserResponse"},"type":"array","title":"Response Get Tool List Api V1 Tools List Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tools/load/url":{"post":{"tags":["tools"],"summary":"Load Tool From Url","operationId":"load_tool_from_url_api_v1_tools_load_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadUrlForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response Load Tool From Url Api V1 Tools Load Url Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tools/export":{"get":{"tags":["tools"],"summary":"Export Tools","operationId":"export_tools_api_v1_tools_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ToolModel"},"type":"array","title":"Response Export Tools Api V1 Tools Export Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tools/create":{"post":{"tags":["tools"],"summary":"Create New Tools","operationId":"create_new_tools_api_v1_tools_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ToolResponse"},{"type":"null"}],"title":"Response Create New Tools Api V1 Tools Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/tools/id/{id}":{"get":{"tags":["tools"],"summary":"Get Tools By Id","operationId":"get_tools_by_id_api_v1_tools_id__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ToolModel"},{"type":"null"}],"title":"Response Get Tools By Id Api V1 Tools Id Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/update":{"post":{"tags":["tools"],"summary":"Update Tools By Id","operationId":"update_tools_by_id_api_v1_tools_id__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToolForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ToolModel"},{"type":"null"}],"title":"Response Update Tools By Id Api V1 Tools Id Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/delete":{"delete":{"tags":["tools"],"summary":"Delete Tools By Id","operationId":"delete_tools_by_id_api_v1_tools_id__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Tools By Id Api V1 Tools Id Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves":{"get":{"tags":["tools"],"summary":"Get Tools Valves By Id","operationId":"get_tools_valves_by_id_api_v1_tools_id__id__valves_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Tools Valves By Id Api V1 Tools Id Id Valves Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves/spec":{"get":{"tags":["tools"],"summary":"Get Tools Valves Spec By Id","operationId":"get_tools_valves_spec_by_id_api_v1_tools_id__id__valves_spec_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Tools Valves Spec By Id Api V1 Tools Id Id Valves Spec Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves/update":{"post":{"tags":["tools"],"summary":"Update Tools Valves By Id","operationId":"update_tools_valves_by_id_api_v1_tools_id__id__valves_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Update Tools Valves By Id Api V1 Tools Id Id Valves Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves/user":{"get":{"tags":["tools"],"summary":"Get Tools User Valves By Id","operationId":"get_tools_user_valves_by_id_api_v1_tools_id__id__valves_user_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Tools User Valves By Id Api V1 Tools Id Id Valves User Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves/user/spec":{"get":{"tags":["tools"],"summary":"Get Tools User Valves Spec By Id","operationId":"get_tools_user_valves_spec_by_id_api_v1_tools_id__id__valves_user_spec_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Tools User Valves Spec By Id Api V1 Tools Id Id Valves User Spec Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tools/id/{id}/valves/user/update":{"post":{"tags":["tools"],"summary":"Update Tools User Valves By Id","operationId":"update_tools_user_valves_by_id_api_v1_tools_id__id__valves_user_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Update Tools User Valves By Id Api V1 Tools Id Id Valves User Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memories/ef":{"get":{"tags":["memories"],"summary":"Get Embeddings","operationId":"get_embeddings_api_v1_memories_ef_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/memories/":{"get":{"tags":["memories"],"summary":"Get Memories","operationId":"get_memories_api_v1_memories__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/MemoryModel"},"type":"array","title":"Response Get Memories Api V1 Memories Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/memories/add":{"post":{"tags":["memories"],"summary":"Add Memory","operationId":"add_memory_api_v1_memories_add_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemoryForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MemoryModel"},{"type":"null"}],"title":"Response Add Memory Api V1 Memories Add Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/memories/query":{"post":{"tags":["memories"],"summary":"Query Memory","operationId":"query_memory_api_v1_memories_query_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryMemoryForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/memories/reset":{"post":{"tags":["memories"],"summary":"Reset Memory From Vector Db","operationId":"reset_memory_from_vector_db_api_v1_memories_reset_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Reset Memory From Vector Db Api V1 Memories Reset Post"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/memories/delete/user":{"delete":{"tags":["memories"],"summary":"Delete Memory By User Id","operationId":"delete_memory_by_user_id_api_v1_memories_delete_user_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Memory By User Id Api V1 Memories Delete User Delete"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/memories/{memory_id}/update":{"post":{"tags":["memories"],"summary":"Update Memory By Id","operationId":"update_memory_by_id_api_v1_memories__memory_id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","title":"Memory Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryUpdateModel"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MemoryModel"},{"type":"null"}],"title":"Response Update Memory By Id Api V1 Memories Memory Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memories/{memory_id}":{"delete":{"tags":["memories"],"summary":"Delete Memory By Id","operationId":"delete_memory_by_id_api_v1_memories__memory_id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","title":"Memory Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Memory By Id Api V1 Memories Memory Id Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/folders/":{"get":{"tags":["folders"],"summary":"Get Folders","operationId":"get_folders_api_v1_folders__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FolderModel"},"type":"array","title":"Response Get Folders Api V1 Folders Get"}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["folders"],"summary":"Create Folder","operationId":"create_folder_api_v1_folders__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/folders/{id}":{"get":{"tags":["folders"],"summary":"Get Folder By Id","operationId":"get_folder_by_id_api_v1_folders__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FolderModel"},{"type":"null"}],"title":"Response Get Folder By Id Api V1 Folders Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["folders"],"summary":"Delete Folder By Id","operationId":"delete_folder_by_id_api_v1_folders__id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/folders/{id}/update":{"post":{"tags":["folders"],"summary":"Update Folder Name By Id","operationId":"update_folder_name_by_id_api_v1_folders__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderUpdateForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/folders/{id}/update/parent":{"post":{"tags":["folders"],"summary":"Update Folder Parent Id By Id","operationId":"update_folder_parent_id_by_id_api_v1_folders__id__update_parent_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderParentIdForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/folders/{id}/update/expanded":{"post":{"tags":["folders"],"summary":"Update Folder Is Expanded By Id","operationId":"update_folder_is_expanded_by_id_api_v1_folders__id__update_expanded_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderIsExpandedForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/groups/":{"get":{"tags":["groups"],"summary":"Get Groups","operationId":"get_groups_api_v1_groups__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/GroupResponse"},"type":"array","title":"Response Get Groups Api V1 Groups Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/groups/create":{"post":{"tags":["groups"],"summary":"Create New Group","operationId":"create_new_group_api_v1_groups_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GroupResponse"},{"type":"null"}],"title":"Response Create New Group Api V1 Groups Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/groups/id/{id}":{"get":{"tags":["groups"],"summary":"Get Group By Id","operationId":"get_group_by_id_api_v1_groups_id__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GroupResponse"},{"type":"null"}],"title":"Response Get Group By Id Api V1 Groups Id Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/groups/id/{id}/update":{"post":{"tags":["groups"],"summary":"Update Group By Id","operationId":"update_group_by_id_api_v1_groups_id__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GroupUpdateForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GroupResponse"},{"type":"null"}],"title":"Response Update Group By Id Api V1 Groups Id Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/groups/id/{id}/users/add":{"post":{"tags":["groups"],"summary":"Add User To Group","operationId":"add_user_to_group_api_v1_groups_id__id__users_add_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserIdsForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GroupResponse"},{"type":"null"}],"title":"Response Add User To Group Api V1 Groups Id Id Users Add Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/groups/id/{id}/users/remove":{"post":{"tags":["groups"],"summary":"Remove Users From Group","operationId":"remove_users_from_group_api_v1_groups_id__id__users_remove_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserIdsForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GroupResponse"},{"type":"null"}],"title":"Response Remove Users From Group Api V1 Groups Id Id Users Remove Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/groups/id/{id}/delete":{"delete":{"tags":["groups"],"summary":"Delete Group By Id","operationId":"delete_group_by_id_api_v1_groups_id__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Group By Id Api V1 Groups Id Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/":{"post":{"tags":["files"],"summary":"Upload File","operationId":"upload_file_api_v1_files__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"process","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Process"}},{"name":"process_in_background","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Process In Background"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_file_api_v1_files__post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileModelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["files"],"summary":"List Files","operationId":"list_files_api_v1_files__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"content","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Content"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FileModelResponse"},"title":"Response List Files Api V1 Files Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/search":{"get":{"tags":["files"],"summary":"Search Files","description":"Search for files by filename with support for wildcard patterns.","operationId":"search_files_api_v1_files_search_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"filename","in":"query","required":true,"schema":{"type":"string","description":"Filename pattern to search for. Supports wildcards such as '*.txt'","title":"Filename"},"description":"Filename pattern to search for. Supports wildcards such as '*.txt'"},{"name":"content","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Content"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FileModelResponse"},"title":"Response Search Files Api V1 Files Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/all":{"delete":{"tags":["files"],"summary":"Delete All Files","operationId":"delete_all_files_api_v1_files_all_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/files/{id}":{"get":{"tags":["files"],"summary":"Get File By Id","operationId":"get_file_by_id_api_v1_files__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FileModel"},{"type":"null"}],"title":"Response Get File By Id Api V1 Files Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["files"],"summary":"Delete File By Id","operationId":"delete_file_by_id_api_v1_files__id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/process/status":{"get":{"tags":["files"],"summary":"Get File Process Status","operationId":"get_file_process_status_api_v1_files__id__process_status_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"stream","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Stream"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/data/content":{"get":{"tags":["files"],"summary":"Get File Data Content By Id","operationId":"get_file_data_content_by_id_api_v1_files__id__data_content_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/data/content/update":{"post":{"tags":["files"],"summary":"Update File Data Content By Id","operationId":"update_file_data_content_by_id_api_v1_files__id__data_content_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/content":{"get":{"tags":["files"],"summary":"Get File Content By Id","operationId":"get_file_content_by_id_api_v1_files__id__content_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}},{"name":"attachment","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Attachment"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/content/html":{"get":{"tags":["files"],"summary":"Get Html File Content By Id","operationId":"get_html_file_content_by_id_api_v1_files__id__content_html_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{id}/content/{file_name}":{"get":{"tags":["files"],"summary":"Get File Content By Id","operationId":"get_file_content_by_id_api_v1_files__id__content__file_name__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/":{"get":{"tags":["functions"],"summary":"Get Functions","operationId":"get_functions_api_v1_functions__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FunctionResponse"},"type":"array","title":"Response Get Functions Api V1 Functions Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/functions/export":{"get":{"tags":["functions"],"summary":"Get Functions","operationId":"get_functions_api_v1_functions_export_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"include_valves","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Valves"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"anyOf":[{"$ref":"#/components/schemas/FunctionModel"},{"$ref":"#/components/schemas/FunctionWithValvesModel"}]},"title":"Response Get Functions Api V1 Functions Export Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/load/url":{"post":{"tags":["functions"],"summary":"Load Function From Url","operationId":"load_function_from_url_api_v1_functions_load_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadUrlForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Response Load Function From Url Api V1 Functions Load Url Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/functions/sync":{"post":{"tags":["functions"],"summary":"Sync Functions","operationId":"sync_functions_api_v1_functions_sync_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncFunctionsForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FunctionWithValvesModel"},"type":"array","title":"Response Sync Functions Api V1 Functions Sync Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/functions/create":{"post":{"tags":["functions"],"summary":"Create New Function","operationId":"create_new_function_api_v1_functions_create_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunctionForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FunctionResponse"},{"type":"null"}],"title":"Response Create New Function Api V1 Functions Create Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/functions/id/{id}":{"get":{"tags":["functions"],"summary":"Get Function By Id","operationId":"get_function_by_id_api_v1_functions_id__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FunctionModel"},{"type":"null"}],"title":"Response Get Function By Id Api V1 Functions Id Id Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/toggle":{"post":{"tags":["functions"],"summary":"Toggle Function By Id","operationId":"toggle_function_by_id_api_v1_functions_id__id__toggle_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FunctionModel"},{"type":"null"}],"title":"Response Toggle Function By Id Api V1 Functions Id Id Toggle Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/toggle/global":{"post":{"tags":["functions"],"summary":"Toggle Global By Id","operationId":"toggle_global_by_id_api_v1_functions_id__id__toggle_global_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FunctionModel"},{"type":"null"}],"title":"Response Toggle Global By Id Api V1 Functions Id Id Toggle Global Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/update":{"post":{"tags":["functions"],"summary":"Update Function By Id","operationId":"update_function_by_id_api_v1_functions_id__id__update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FunctionForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FunctionModel"},{"type":"null"}],"title":"Response Update Function By Id Api V1 Functions Id Id Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/delete":{"delete":{"tags":["functions"],"summary":"Delete Function By Id","operationId":"delete_function_by_id_api_v1_functions_id__id__delete_delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Function By Id Api V1 Functions Id Id Delete Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves":{"get":{"tags":["functions"],"summary":"Get Function Valves By Id","operationId":"get_function_valves_by_id_api_v1_functions_id__id__valves_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Function Valves By Id Api V1 Functions Id Id Valves Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves/spec":{"get":{"tags":["functions"],"summary":"Get Function Valves Spec By Id","operationId":"get_function_valves_spec_by_id_api_v1_functions_id__id__valves_spec_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Function Valves Spec By Id Api V1 Functions Id Id Valves Spec Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves/update":{"post":{"tags":["functions"],"summary":"Update Function Valves By Id","operationId":"update_function_valves_by_id_api_v1_functions_id__id__valves_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Update Function Valves By Id Api V1 Functions Id Id Valves Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves/user":{"get":{"tags":["functions"],"summary":"Get Function User Valves By Id","operationId":"get_function_user_valves_by_id_api_v1_functions_id__id__valves_user_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Function User Valves By Id Api V1 Functions Id Id Valves User Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves/user/spec":{"get":{"tags":["functions"],"summary":"Get Function User Valves Spec By Id","operationId":"get_function_user_valves_spec_by_id_api_v1_functions_id__id__valves_user_spec_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Get Function User Valves Spec By Id Api V1 Functions Id Id Valves User Spec Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/functions/id/{id}/valves/user/update":{"post":{"tags":["functions"],"summary":"Update Function User Valves By Id","operationId":"update_function_user_valves_by_id_api_v1_functions_id__id__valves_user_update_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Response Update Function User Valves By Id Api V1 Functions Id Id Valves User Update Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/evaluations/config":{"get":{"tags":["evaluations"],"summary":"Get Config","operationId":"get_config_api_v1_evaluations_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"tags":["evaluations"],"summary":"Update Config","operationId":"update_config_api_v1_evaluations_config_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateConfigForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedbacks/all":{"get":{"tags":["evaluations"],"summary":"Get All Feedbacks","operationId":"get_all_feedbacks_api_v1_evaluations_feedbacks_all_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FeedbackUserResponse"},"type":"array","title":"Response Get All Feedbacks Api V1 Evaluations Feedbacks All Get"}}}}},"security":[{"HTTPBearer":[]}]},"delete":{"tags":["evaluations"],"summary":"Delete All Feedbacks","operationId":"delete_all_feedbacks_api_v1_evaluations_feedbacks_all_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedbacks/all/export":{"get":{"tags":["evaluations"],"summary":"Get All Feedbacks","operationId":"get_all_feedbacks_api_v1_evaluations_feedbacks_all_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FeedbackModel"},"type":"array","title":"Response Get All Feedbacks Api V1 Evaluations Feedbacks All Export Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedbacks/user":{"get":{"tags":["evaluations"],"summary":"Get Feedbacks","operationId":"get_feedbacks_api_v1_evaluations_feedbacks_user_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/FeedbackUserResponse"},"type":"array","title":"Response Get Feedbacks Api V1 Evaluations Feedbacks User Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedbacks":{"delete":{"tags":["evaluations"],"summary":"Delete Feedbacks","operationId":"delete_feedbacks_api_v1_evaluations_feedbacks_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"boolean","title":"Response Delete Feedbacks Api V1 Evaluations Feedbacks Delete"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedback":{"post":{"tags":["evaluations"],"summary":"Create Feedback","operationId":"create_feedback_api_v1_evaluations_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackModel"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/evaluations/feedback/{id}":{"get":{"tags":["evaluations"],"summary":"Get Feedback By Id","operationId":"get_feedback_by_id_api_v1_evaluations_feedback__id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackModel"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["evaluations"],"summary":"Update Feedback By Id","operationId":"update_feedback_by_id_api_v1_evaluations_feedback__id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackForm"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackModel"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["evaluations"],"summary":"Delete Feedback By Id","operationId":"delete_feedback_by_id_api_v1_evaluations_feedback__id__delete","security":[{"HTTPBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","title":"Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/utils/gravatar":{"get":{"tags":["utils"],"summary":"Get Gravatar","operationId":"get_gravatar_api_v1_utils_gravatar_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/utils/code/format":{"post":{"tags":["utils"],"summary":"Format Code","operationId":"format_code_api_v1_utils_code_format_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/utils/code/execute":{"post":{"tags":["utils"],"summary":"Execute Code","operationId":"execute_code_api_v1_utils_code_execute_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CodeForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/utils/markdown":{"post":{"tags":["utils"],"summary":"Get Html From Markdown","operationId":"get_html_from_markdown_api_v1_utils_markdown_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkdownForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/utils/pdf":{"post":{"tags":["utils"],"summary":"Download Chat As Pdf","operationId":"download_chat_as_pdf_api_v1_utils_pdf_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatTitleMessagesForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/utils/db/download":{"get":{"tags":["utils"],"summary":"Download Db","operationId":"download_db_api_v1_utils_db_download_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/utils/litellm/config":{"get":{"tags":["utils"],"summary":"Download Litellm Config Yaml","operationId":"download_litellm_config_yaml_api_v1_utils_litellm_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/models":{"get":{"summary":"Get Models","operationId":"get_models_api_v1_models_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/models":{"get":{"summary":"Get Models","operationId":"get_models_api_models_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/models/base":{"get":{"summary":"Get Base Models","operationId":"get_base_models_api_models_base_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/embeddings":{"post":{"summary":"Embeddings","description":"OpenAI-compatible embeddings endpoint.\n\nThis handler:\n - Performs user/model checks and dispatches to the correct backend.\n - Supports OpenAI, Ollama, arena models, pipelines, and any compatible provider.\n\nArgs:\n request (Request): Request context.\n form_data (dict): OpenAI-like payload (e.g., {\"model\": \"...\", \"input\": [...]})\n user (UserModel): Authenticated user.\n\nReturns:\n dict: OpenAI-compatible embeddings response.","operationId":"embeddings_api_v1_embeddings_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/embeddings":{"post":{"summary":"Embeddings","description":"OpenAI-compatible embeddings endpoint.\n\nThis handler:\n - Performs user/model checks and dispatches to the correct backend.\n - Supports OpenAI, Ollama, arena models, pipelines, and any compatible provider.\n\nArgs:\n request (Request): Request context.\n form_data (dict): OpenAI-like payload (e.g., {\"model\": \"...\", \"input\": [...]})\n user (UserModel): Authenticated user.\n\nReturns:\n dict: OpenAI-compatible embeddings response.","operationId":"embeddings_api_embeddings_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/v1/chat/completions":{"post":{"summary":"Chat Completion","operationId":"chat_completion_api_v1_chat_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/chat/completions":{"post":{"summary":"Chat Completion","operationId":"chat_completion_api_chat_completions_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/chat/completed":{"post":{"summary":"Chat Completed","operationId":"chat_completed_api_chat_completed_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Form Data"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/chat/actions/{action_id}":{"post":{"summary":"Chat Action","operationId":"chat_action_api_chat_actions__action_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"string","title":"Action Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Form Data"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tasks/stop/{task_id}":{"post":{"summary":"Stop Task Endpoint","operationId":"stop_task_endpoint_api_tasks_stop__task_id__post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tasks":{"get":{"summary":"List Tasks Endpoint","operationId":"list_tasks_endpoint_api_tasks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/tasks/chat/{chat_id}":{"get":{"summary":"List Tasks By Chat Id Endpoint","operationId":"list_tasks_by_chat_id_endpoint_api_tasks_chat__chat_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"chat_id","in":"path","required":true,"schema":{"type":"string","title":"Chat Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/config":{"get":{"summary":"Get App Config","operationId":"get_app_config_api_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/webhook":{"get":{"summary":"Get Webhook Url","operationId":"get_webhook_url_api_webhook_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]},"post":{"summary":"Update Webhook Url","operationId":"update_webhook_url_api_webhook_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UrlForm"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"HTTPBearer":[]}]}},"/api/version":{"get":{"summary":"Get App Version","operationId":"get_app_version_api_version_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/version/updates":{"get":{"summary":"Get App Latest Release Version","operationId":"get_app_latest_release_version_api_version_updates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/api/changelog":{"get":{"summary":"Get App Changelog","operationId":"get_app_changelog_api_changelog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/usage":{"get":{"summary":"Get Current Usage","description":"Get current usage statistics for Open WebUI.\nThis is an experimental endpoint and subject to change.","operationId":"get_current_usage_api_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}},"security":[{"HTTPBearer":[]}]}},"/oauth/{provider}/login":{"get":{"summary":"Oauth Login","operationId":"oauth_login_oauth__provider__login_get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/{provider}/callback":{"get":{"summary":"Oauth Callback","operationId":"oauth_callback_oauth__provider__callback_get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/manifest.json":{"get":{"summary":"Get Manifest Json","operationId":"get_manifest_json_manifest_json_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/opensearch.xml":{"get":{"summary":"Get Opensearch Xml","operationId":"get_opensearch_xml_opensearch_xml_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Healthcheck","operationId":"healthcheck_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/db":{"get":{"summary":"Healthcheck With Db","operationId":"healthcheck_with_db_health_db_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/cache/{path}":{"get":{"summary":"Serve Cache File","operationId":"serve_cache_file_cache__path__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"path","in":"path","required":true,"schema":{"type":"string","title":"Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AddMemoryForm":{"properties":{"content":{"type":"string","title":"Content"}},"type":"object","required":["content"],"title":"AddMemoryForm"},"AddPipelineForm":{"properties":{"url":{"type":"string","title":"Url"},"urlIdx":{"type":"integer","title":"Urlidx"}},"type":"object","required":["url","urlIdx"],"title":"AddPipelineForm"},"AddUserForm":{"properties":{"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"profile_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Image Url","default":"/user.png"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role","default":"pending"}},"type":"object","required":["name","email","password"],"title":"AddUserForm"},"AdminConfig":{"properties":{"SHOW_ADMIN_DETAILS":{"type":"boolean","title":"Show Admin Details"},"WEBUI_URL":{"type":"string","title":"Webui Url"},"ENABLE_SIGNUP":{"type":"boolean","title":"Enable Signup"},"ENABLE_API_KEY":{"type":"boolean","title":"Enable Api Key"},"ENABLE_API_KEY_ENDPOINT_RESTRICTIONS":{"type":"boolean","title":"Enable Api Key Endpoint Restrictions"},"API_KEY_ALLOWED_ENDPOINTS":{"type":"string","title":"Api Key Allowed Endpoints"},"DEFAULT_USER_ROLE":{"type":"string","title":"Default User Role"},"JWT_EXPIRES_IN":{"type":"string","title":"Jwt Expires In"},"ENABLE_COMMUNITY_SHARING":{"type":"boolean","title":"Enable Community Sharing"},"ENABLE_MESSAGE_RATING":{"type":"boolean","title":"Enable Message Rating"},"ENABLE_CHANNELS":{"type":"boolean","title":"Enable Channels"},"ENABLE_NOTES":{"type":"boolean","title":"Enable Notes"},"ENABLE_USER_WEBHOOKS":{"type":"boolean","title":"Enable User Webhooks"},"PENDING_USER_OVERLAY_TITLE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pending User Overlay Title"},"PENDING_USER_OVERLAY_CONTENT":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pending User Overlay Content"},"RESPONSE_WATERMARK":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Watermark"}},"type":"object","required":["SHOW_ADMIN_DETAILS","WEBUI_URL","ENABLE_SIGNUP","ENABLE_API_KEY","ENABLE_API_KEY_ENDPOINT_RESTRICTIONS","API_KEY_ALLOWED_ENDPOINTS","DEFAULT_USER_ROLE","JWT_EXPIRES_IN","ENABLE_COMMUNITY_SHARING","ENABLE_MESSAGE_RATING","ENABLE_CHANNELS","ENABLE_NOTES","ENABLE_USER_WEBHOOKS"],"title":"AdminConfig"},"ApiKey":{"properties":{"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key"}},"type":"object","title":"ApiKey"},"AudioConfigUpdateForm":{"properties":{"tts":{"$ref":"#/components/schemas/TTSConfigForm"},"stt":{"$ref":"#/components/schemas/STTConfigForm"}},"type":"object","required":["tts","stt"],"title":"AudioConfigUpdateForm"},"Automatic1111ConfigForm":{"properties":{"AUTOMATIC1111_BASE_URL":{"type":"string","title":"Automatic1111 Base Url"},"AUTOMATIC1111_API_AUTH":{"type":"string","title":"Automatic1111 Api Auth"},"AUTOMATIC1111_CFG_SCALE":{"anyOf":[{"type":"string"},{"type":"number"},{"type":"integer"},{"type":"null"}],"title":"Automatic1111 Cfg Scale"},"AUTOMATIC1111_SAMPLER":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Automatic1111 Sampler"},"AUTOMATIC1111_SCHEDULER":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Automatic1111 Scheduler"}},"type":"object","required":["AUTOMATIC1111_BASE_URL","AUTOMATIC1111_API_AUTH","AUTOMATIC1111_CFG_SCALE","AUTOMATIC1111_SAMPLER","AUTOMATIC1111_SCHEDULER"],"title":"Automatic1111ConfigForm"},"AzureOpenAIConfigForm":{"properties":{"url":{"type":"string","title":"Url"},"key":{"type":"string","title":"Key"},"version":{"type":"string","title":"Version"}},"type":"object","required":["url","key","version"],"title":"AzureOpenAIConfigForm"},"BannerModel":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"},"content":{"type":"string","title":"Content"},"dismissible":{"type":"boolean","title":"Dismissible"},"timestamp":{"type":"integer","title":"Timestamp"}},"type":"object","required":["id","type","content","dismissible","timestamp"],"title":"BannerModel"},"BatchProcessFilesForm":{"properties":{"files":{"items":{"$ref":"#/components/schemas/FileModel"},"type":"array","title":"Files"},"collection_name":{"type":"string","title":"Collection Name"}},"type":"object","required":["files","collection_name"],"title":"BatchProcessFilesForm"},"BatchProcessFilesResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/BatchProcessFilesResult"},"type":"array","title":"Results"},"errors":{"items":{"$ref":"#/components/schemas/BatchProcessFilesResult"},"type":"array","title":"Errors"}},"type":"object","required":["results","errors"],"title":"BatchProcessFilesResponse"},"BatchProcessFilesResult":{"properties":{"file_id":{"type":"string","title":"File Id"},"status":{"type":"string","title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["file_id","status"],"title":"BatchProcessFilesResult"},"Body_transcription_api_v1_audio_transcriptions_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"language":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Language"}},"type":"object","required":["file"],"title":"Body_transcription_api_v1_audio_transcriptions_post"},"Body_upload_file_api_v1_files__post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"string"},{"type":"null"}],"title":"Metadata"}},"type":"object","required":["file"],"title":"Body_upload_file_api_v1_files__post"},"Body_upload_model_ollama_models_upload__url_idx__post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_model_ollama_models_upload__url_idx__post"},"Body_upload_model_ollama_models_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_model_ollama_models_upload_post"},"Body_upload_pipeline_api_v1_pipelines_upload_post":{"properties":{"urlIdx":{"type":"integer","title":"Urlidx"},"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["urlIdx","file"],"title":"Body_upload_pipeline_api_v1_pipelines_upload_post"},"ChannelForm":{"properties":{"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["name"],"title":"ChannelForm"},"ChannelModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","name","created_at","updated_at"],"title":"ChannelModel"},"ChatFolderIdForm":{"properties":{"folder_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Id"}},"type":"object","title":"ChatFolderIdForm"},"ChatForm":{"properties":{"chat":{"additionalProperties":true,"type":"object","title":"Chat"},"folder_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Id"}},"type":"object","required":["chat"],"title":"ChatForm"},"ChatImportForm":{"properties":{"chat":{"additionalProperties":true,"type":"object","title":"Chat"},"folder_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Id"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta","default":{}},"pinned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pinned","default":false},"created_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["chat"],"title":"ChatImportForm"},"ChatPermissions":{"properties":{"controls":{"type":"boolean","title":"Controls","default":true},"valves":{"type":"boolean","title":"Valves","default":true},"system_prompt":{"type":"boolean","title":"System Prompt","default":true},"params":{"type":"boolean","title":"Params","default":true},"file_upload":{"type":"boolean","title":"File Upload","default":true},"delete":{"type":"boolean","title":"Delete","default":true},"delete_message":{"type":"boolean","title":"Delete Message","default":true},"continue_response":{"type":"boolean","title":"Continue Response","default":true},"regenerate_response":{"type":"boolean","title":"Regenerate Response","default":true},"rate_response":{"type":"boolean","title":"Rate Response","default":true},"edit":{"type":"boolean","title":"Edit","default":true},"share":{"type":"boolean","title":"Share","default":true},"export":{"type":"boolean","title":"Export","default":true},"stt":{"type":"boolean","title":"Stt","default":true},"tts":{"type":"boolean","title":"Tts","default":true},"call":{"type":"boolean","title":"Call","default":true},"multiple_models":{"type":"boolean","title":"Multiple Models","default":true},"temporary":{"type":"boolean","title":"Temporary","default":true},"temporary_enforced":{"type":"boolean","title":"Temporary Enforced","default":false}},"type":"object","title":"ChatPermissions"},"ChatResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"title":{"type":"string","title":"Title"},"chat":{"additionalProperties":true,"type":"object","title":"Chat"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"},"share_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Share Id"},"archived":{"type":"boolean","title":"Archived"},"pinned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pinned","default":false},"meta":{"additionalProperties":true,"type":"object","title":"Meta","default":{}},"folder_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Folder Id"}},"type":"object","required":["id","user_id","title","chat","updated_at","created_at","archived"],"title":"ChatResponse"},"ChatTitleIdResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","title","updated_at","created_at"],"title":"ChatTitleIdResponse"},"ChatTitleMessagesForm":{"properties":{"title":{"type":"string","title":"Title"},"messages":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Messages"}},"type":"object","required":["title","messages"],"title":"ChatTitleMessagesForm"},"CloneForm":{"properties":{"title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Title"}},"type":"object","title":"CloneForm"},"CodeForm":{"properties":{"code":{"type":"string","title":"Code"}},"type":"object","required":["code"],"title":"CodeForm"},"CodeInterpreterConfigForm":{"properties":{"ENABLE_CODE_EXECUTION":{"type":"boolean","title":"Enable Code Execution"},"CODE_EXECUTION_ENGINE":{"type":"string","title":"Code Execution Engine"},"CODE_EXECUTION_JUPYTER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Execution Jupyter Url"},"CODE_EXECUTION_JUPYTER_AUTH":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Execution Jupyter Auth"},"CODE_EXECUTION_JUPYTER_AUTH_TOKEN":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Execution Jupyter Auth Token"},"CODE_EXECUTION_JUPYTER_AUTH_PASSWORD":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Execution Jupyter Auth Password"},"CODE_EXECUTION_JUPYTER_TIMEOUT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Code Execution Jupyter Timeout"},"ENABLE_CODE_INTERPRETER":{"type":"boolean","title":"Enable Code Interpreter"},"CODE_INTERPRETER_ENGINE":{"type":"string","title":"Code Interpreter Engine"},"CODE_INTERPRETER_PROMPT_TEMPLATE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Interpreter Prompt Template"},"CODE_INTERPRETER_JUPYTER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Interpreter Jupyter Url"},"CODE_INTERPRETER_JUPYTER_AUTH":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Interpreter Jupyter Auth"},"CODE_INTERPRETER_JUPYTER_AUTH_TOKEN":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Interpreter Jupyter Auth Token"},"CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Interpreter Jupyter Auth Password"},"CODE_INTERPRETER_JUPYTER_TIMEOUT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Code Interpreter Jupyter Timeout"}},"type":"object","required":["ENABLE_CODE_EXECUTION","CODE_EXECUTION_ENGINE","CODE_EXECUTION_JUPYTER_URL","CODE_EXECUTION_JUPYTER_AUTH","CODE_EXECUTION_JUPYTER_AUTH_TOKEN","CODE_EXECUTION_JUPYTER_AUTH_PASSWORD","CODE_EXECUTION_JUPYTER_TIMEOUT","ENABLE_CODE_INTERPRETER","CODE_INTERPRETER_ENGINE","CODE_INTERPRETER_PROMPT_TEMPLATE","CODE_INTERPRETER_JUPYTER_URL","CODE_INTERPRETER_JUPYTER_AUTH","CODE_INTERPRETER_JUPYTER_AUTH_TOKEN","CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD","CODE_INTERPRETER_JUPYTER_TIMEOUT"],"title":"CodeInterpreterConfigForm"},"ComfyUIConfigForm":{"properties":{"COMFYUI_BASE_URL":{"type":"string","title":"Comfyui Base Url"},"COMFYUI_API_KEY":{"type":"string","title":"Comfyui Api Key"},"COMFYUI_WORKFLOW":{"type":"string","title":"Comfyui Workflow"},"COMFYUI_WORKFLOW_NODES":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Comfyui Workflow Nodes"}},"type":"object","required":["COMFYUI_BASE_URL","COMFYUI_API_KEY","COMFYUI_WORKFLOW","COMFYUI_WORKFLOW_NODES"],"title":"ComfyUIConfigForm"},"ConnectionsConfigForm":{"properties":{"ENABLE_DIRECT_CONNECTIONS":{"type":"boolean","title":"Enable Direct Connections"},"ENABLE_BASE_MODELS_CACHE":{"type":"boolean","title":"Enable Base Models Cache"}},"type":"object","required":["ENABLE_DIRECT_CONNECTIONS","ENABLE_BASE_MODELS_CACHE"],"title":"ConnectionsConfigForm"},"ContentForm":{"properties":{"content":{"type":"string","title":"Content"}},"type":"object","required":["content"],"title":"ContentForm"},"CopyModelForm":{"properties":{"source":{"type":"string","title":"Source"},"destination":{"type":"string","title":"Destination"}},"type":"object","required":["source","destination"],"title":"CopyModelForm"},"CreateModelForm":{"properties":{"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"stream":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Stream"},"path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path"}},"additionalProperties":true,"type":"object","title":"CreateModelForm"},"DeleteForm":{"properties":{"collection_name":{"type":"string","title":"Collection Name"},"file_id":{"type":"string","title":"File Id"}},"type":"object","required":["collection_name","file_id"],"title":"DeleteForm"},"DeletePipelineForm":{"properties":{"id":{"type":"string","title":"Id"},"urlIdx":{"type":"integer","title":"Urlidx"}},"type":"object","required":["id","urlIdx"],"title":"DeletePipelineForm"},"EmbeddingModelUpdateForm":{"properties":{"openai_config":{"anyOf":[{"$ref":"#/components/schemas/open_webui__routers__retrieval__OpenAIConfigForm"},{"type":"null"}]},"ollama_config":{"anyOf":[{"$ref":"#/components/schemas/open_webui__routers__retrieval__OllamaConfigForm"},{"type":"null"}]},"azure_openai_config":{"anyOf":[{"$ref":"#/components/schemas/AzureOpenAIConfigForm"},{"type":"null"}]},"embedding_engine":{"type":"string","title":"Embedding Engine"},"embedding_model":{"type":"string","title":"Embedding Model"},"embedding_batch_size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Embedding Batch Size","default":1}},"type":"object","required":["embedding_engine","embedding_model"],"title":"EmbeddingModelUpdateForm"},"EventForm":{"properties":{"type":{"type":"string","title":"Type"},"data":{"additionalProperties":true,"type":"object","title":"Data"}},"type":"object","required":["type","data"],"title":"EventForm"},"FeaturesPermissions":{"properties":{"direct_tool_servers":{"type":"boolean","title":"Direct Tool Servers","default":false},"web_search":{"type":"boolean","title":"Web Search","default":true},"image_generation":{"type":"boolean","title":"Image Generation","default":true},"code_interpreter":{"type":"boolean","title":"Code Interpreter","default":true},"notes":{"type":"boolean","title":"Notes","default":true}},"type":"object","title":"FeaturesPermissions"},"FeedbackForm":{"properties":{"type":{"type":"string","title":"Type"},"data":{"anyOf":[{"$ref":"#/components/schemas/RatingData"},{"type":"null"}]},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"snapshot":{"anyOf":[{"$ref":"#/components/schemas/SnapshotData"},{"type":"null"}]}},"additionalProperties":true,"type":"object","required":["type"],"title":"FeedbackForm"},"FeedbackModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"version":{"type":"integer","title":"Version"},"type":{"type":"string","title":"Type"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"snapshot":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Snapshot"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","version","type","created_at","updated_at"],"title":"FeedbackModel"},"FeedbackUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"version":{"type":"integer","title":"Version"},"type":{"type":"string","title":"Type"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__routers__evaluations__UserResponse"},{"type":"null"}]}},"type":"object","required":["id","user_id","version","type","created_at","updated_at"],"title":"FeedbackUserResponse"},"FileMeta":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"content_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Type"},"size":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Size"}},"additionalProperties":true,"type":"object","title":"FileMeta"},"FileMetadataResponse":{"properties":{"id":{"type":"string","title":"Id"},"meta":{"additionalProperties":true,"type":"object","title":"Meta"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","meta","created_at","updated_at"],"title":"FileMetadataResponse"},"FileModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hash"},"filename":{"type":"string","title":"Filename"},"path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Path"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["id","user_id","filename","created_at","updated_at"],"title":"FileModel"},"FileModelResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hash"},"filename":{"type":"string","title":"Filename"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"$ref":"#/components/schemas/FileMeta"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"additionalProperties":true,"type":"object","required":["id","user_id","filename","meta","created_at","updated_at"],"title":"FileModelResponse"},"FolderForm":{"properties":{"name":{"type":"string","title":"Name"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"}},"additionalProperties":true,"type":"object","required":["name"],"title":"FolderForm"},"FolderIsExpandedForm":{"properties":{"is_expanded":{"type":"boolean","title":"Is Expanded"}},"type":"object","required":["is_expanded"],"title":"FolderIsExpandedForm"},"FolderModel":{"properties":{"id":{"type":"string","title":"Id"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"items":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Items"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"is_expanded":{"type":"boolean","title":"Is Expanded","default":false},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","name","created_at","updated_at"],"title":"FolderModel"},"FolderParentIdForm":{"properties":{"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"}},"type":"object","title":"FolderParentIdForm"},"FolderUpdateForm":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"}},"additionalProperties":true,"type":"object","title":"FolderUpdateForm"},"FunctionForm":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"meta":{"$ref":"#/components/schemas/FunctionMeta"}},"type":"object","required":["id","name","content","meta"],"title":"FunctionForm"},"FunctionMeta":{"properties":{"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"manifest":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Manifest","default":{}}},"type":"object","title":"FunctionMeta"},"FunctionModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"type":{"type":"string","title":"Type"},"content":{"type":"string","title":"Content"},"meta":{"$ref":"#/components/schemas/FunctionMeta"},"is_active":{"type":"boolean","title":"Is Active","default":false},"is_global":{"type":"boolean","title":"Is Global","default":false},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","type","content","meta","updated_at","created_at"],"title":"FunctionModel"},"FunctionResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"type":{"type":"string","title":"Type"},"name":{"type":"string","title":"Name"},"meta":{"$ref":"#/components/schemas/FunctionMeta"},"is_active":{"type":"boolean","title":"Is Active"},"is_global":{"type":"boolean","title":"Is Global"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","type","name","meta","is_active","is_global","updated_at","created_at"],"title":"FunctionResponse"},"FunctionWithValvesModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"type":{"type":"string","title":"Type"},"content":{"type":"string","title":"Content"},"meta":{"$ref":"#/components/schemas/FunctionMeta"},"valves":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Valves"},"is_active":{"type":"boolean","title":"Is Active","default":false},"is_global":{"type":"boolean","title":"Is Global","default":false},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","type","content","meta","updated_at","created_at"],"title":"FunctionWithValvesModel"},"GeminiConfigForm":{"properties":{"GEMINI_API_BASE_URL":{"type":"string","title":"Gemini Api Base Url"},"GEMINI_API_KEY":{"type":"string","title":"Gemini Api Key"}},"type":"object","required":["GEMINI_API_BASE_URL","GEMINI_API_KEY"],"title":"GeminiConfigForm"},"GenerateCompletionForm":{"properties":{"model":{"type":"string","title":"Model"},"prompt":{"type":"string","title":"Prompt"},"suffix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suffix"},"images":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Images"},"format":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"string"},{"type":"null"}],"title":"Format"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Options"},"system":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"System"},"template":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template"},"context":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Context"},"stream":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Stream","default":true},"raw":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Raw"},"keep_alive":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Keep Alive"}},"type":"object","required":["model","prompt"],"title":"GenerateCompletionForm"},"GenerateEmbedForm":{"properties":{"model":{"type":"string","title":"Model"},"input":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"string"}],"title":"Input"},"truncate":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Truncate"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Options"},"keep_alive":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Keep Alive"}},"type":"object","required":["model","input"],"title":"GenerateEmbedForm"},"GenerateEmbeddingsForm":{"properties":{"model":{"type":"string","title":"Model"},"prompt":{"type":"string","title":"Prompt"},"options":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Options"},"keep_alive":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Keep Alive"}},"type":"object","required":["model","prompt"],"title":"GenerateEmbeddingsForm"},"GenerateImageForm":{"properties":{"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"prompt":{"type":"string","title":"Prompt"},"size":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Size"},"n":{"type":"integer","title":"N","default":1},"negative_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Negative Prompt"}},"type":"object","required":["prompt"],"title":"GenerateImageForm"},"GroupForm":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"}},"type":"object","required":["name","description"],"title":"GroupForm"},"GroupResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"user_ids":{"items":{"type":"string"},"type":"array","title":"User Ids","default":[]},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","name","description","created_at","updated_at"],"title":"GroupResponse"},"GroupUpdateForm":{"properties":{"user_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"User Ids"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"}},"type":"object","required":["name","description"],"title":"GroupUpdateForm"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImageConfigForm":{"properties":{"MODEL":{"type":"string","title":"Model"},"IMAGE_SIZE":{"type":"string","title":"Image Size"},"IMAGE_STEPS":{"type":"integer","title":"Image Steps"}},"type":"object","required":["MODEL","IMAGE_SIZE","IMAGE_STEPS"],"title":"ImageConfigForm"},"ImportConfigForm":{"properties":{"config":{"additionalProperties":true,"type":"object","title":"Config"}},"type":"object","required":["config"],"title":"ImportConfigForm"},"KnowledgeFileIdForm":{"properties":{"file_id":{"type":"string","title":"File Id"}},"type":"object","required":["file_id"],"title":"KnowledgeFileIdForm"},"KnowledgeFilesResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"files":{"items":{"$ref":"#/components/schemas/FileMetadataResponse"},"type":"array","title":"Files"}},"type":"object","required":["id","user_id","name","description","created_at","updated_at","files"],"title":"KnowledgeFilesResponse"},"KnowledgeForm":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["name","description"],"title":"KnowledgeForm"},"KnowledgeResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"files":{"anyOf":[{"items":{"anyOf":[{"$ref":"#/components/schemas/FileMetadataResponse"},{"additionalProperties":true,"type":"object"}]},"type":"array"},{"type":"null"}],"title":"Files"}},"type":"object","required":["id","user_id","name","description","created_at","updated_at"],"title":"KnowledgeResponse"},"KnowledgeUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__models__users__UserResponse"},{"type":"null"}]},"files":{"anyOf":[{"items":{"anyOf":[{"$ref":"#/components/schemas/FileMetadataResponse"},{"additionalProperties":true,"type":"object"}]},"type":"array"},{"type":"null"}],"title":"Files"}},"type":"object","required":["id","user_id","name","description","created_at","updated_at"],"title":"KnowledgeUserResponse"},"LdapConfigForm":{"properties":{"enable_ldap":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Ldap"}},"type":"object","title":"LdapConfigForm"},"LdapForm":{"properties":{"user":{"type":"string","title":"User"},"password":{"type":"string","title":"Password"}},"type":"object","required":["user","password"],"title":"LdapForm"},"LdapServerConfig":{"properties":{"label":{"type":"string","title":"Label"},"host":{"type":"string","title":"Host"},"port":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Port"},"attribute_for_mail":{"type":"string","title":"Attribute For Mail","default":"mail"},"attribute_for_username":{"type":"string","title":"Attribute For Username","default":"uid"},"app_dn":{"type":"string","title":"App Dn"},"app_dn_password":{"type":"string","title":"App Dn Password"},"search_base":{"type":"string","title":"Search Base"},"search_filters":{"type":"string","title":"Search Filters","default":""},"use_tls":{"type":"boolean","title":"Use Tls","default":true},"certificate_path":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Certificate Path"},"validate_cert":{"type":"boolean","title":"Validate Cert","default":true},"ciphers":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ciphers","default":"ALL"}},"type":"object","required":["label","host","app_dn","app_dn_password","search_base"],"title":"LdapServerConfig"},"LoadUrlForm":{"properties":{"url":{"type":"string","maxLength":2083,"minLength":1,"format":"uri","title":"Url"}},"type":"object","required":["url"],"title":"LoadUrlForm"},"MarkdownForm":{"properties":{"md":{"type":"string","title":"Md"}},"type":"object","required":["md"],"title":"MarkdownForm"},"MemoryModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"content":{"type":"string","title":"Content"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","content","updated_at","created_at"],"title":"MemoryModel"},"MemoryUpdateModel":{"properties":{"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"}},"type":"object","title":"MemoryUpdateModel"},"MessageModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Id"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"content":{"type":"string","title":"Content"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","content","created_at","updated_at"],"title":"MessageModel"},"MessageUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"channel_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Channel Id"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"content":{"type":"string","title":"Content"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"latest_reply_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latest Reply At"},"reply_count":{"type":"integer","title":"Reply Count"},"reactions":{"items":{"$ref":"#/components/schemas/Reactions"},"type":"array","title":"Reactions"},"user":{"$ref":"#/components/schemas/UserNameResponse"}},"type":"object","required":["id","user_id","content","created_at","updated_at","latest_reply_at","reply_count","reactions","user"],"title":"MessageUserResponse"},"ModelForm":{"properties":{"id":{"type":"string","title":"Id"},"base_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Model Id"},"name":{"type":"string","title":"Name"},"meta":{"$ref":"#/components/schemas/ModelMeta"},"params":{"$ref":"#/components/schemas/ModelParams"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"is_active":{"type":"boolean","title":"Is Active","default":true}},"type":"object","required":["id","name","meta","params"],"title":"ModelForm"},"ModelMeta":{"properties":{"profile_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Image Url","default":"/static/favicon.png"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"capabilities":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Capabilities"}},"additionalProperties":true,"type":"object","title":"ModelMeta"},"ModelModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"base_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Model Id"},"name":{"type":"string","title":"Name"},"params":{"$ref":"#/components/schemas/ModelParams"},"meta":{"$ref":"#/components/schemas/ModelMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"is_active":{"type":"boolean","title":"Is Active"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","params","meta","is_active","updated_at","created_at"],"title":"ModelModel"},"ModelNameForm":{"properties":{"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"}},"additionalProperties":true,"type":"object","title":"ModelNameForm"},"ModelParams":{"properties":{},"additionalProperties":true,"type":"object","title":"ModelParams"},"ModelResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"base_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Model Id"},"name":{"type":"string","title":"Name"},"params":{"$ref":"#/components/schemas/ModelParams"},"meta":{"$ref":"#/components/schemas/ModelMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"is_active":{"type":"boolean","title":"Is Active"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","params","meta","is_active","updated_at","created_at"],"title":"ModelResponse"},"ModelUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"base_model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Base Model Id"},"name":{"type":"string","title":"Name"},"params":{"$ref":"#/components/schemas/ModelParams"},"meta":{"$ref":"#/components/schemas/ModelMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"is_active":{"type":"boolean","title":"Is Active"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__models__users__UserResponse"},{"type":"null"}]}},"type":"object","required":["id","user_id","name","params","meta","is_active","updated_at","created_at"],"title":"ModelUserResponse"},"ModelsConfigForm":{"properties":{"DEFAULT_MODELS":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Default Models"},"MODEL_ORDER_LIST":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Model Order List"}},"type":"object","required":["DEFAULT_MODELS","MODEL_ORDER_LIST"],"title":"ModelsConfigForm"},"NoteForm":{"properties":{"title":{"type":"string","title":"Title"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["title"],"title":"NoteForm"},"NoteModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"title":{"type":"string","title":"Title"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"}},"type":"object","required":["id","user_id","title","created_at","updated_at"],"title":"NoteModel"},"NoteTitleIdResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","title","updated_at","created_at"],"title":"NoteTitleIdResponse"},"NoteUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"title":{"type":"string","title":"Title"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"created_at":{"type":"integer","title":"Created At"},"updated_at":{"type":"integer","title":"Updated At"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__models__users__UserResponse"},{"type":"null"}]}},"type":"object","required":["id","user_id","title","created_at","updated_at"],"title":"NoteUserResponse"},"ProcessFileForm":{"properties":{"file_id":{"type":"string","title":"File Id"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content"},"collection_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Name"}},"type":"object","required":["file_id"],"title":"ProcessFileForm"},"ProcessTextForm":{"properties":{"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"collection_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Name"}},"type":"object","required":["name","content"],"title":"ProcessTextForm"},"ProcessUrlForm":{"properties":{"collection_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Collection Name"},"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"ProcessUrlForm"},"PromptForm":{"properties":{"command":{"type":"string","title":"Command"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["command","title","content"],"title":"PromptForm"},"PromptModel":{"properties":{"command":{"type":"string","title":"Command"},"user_id":{"type":"string","title":"User Id"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"timestamp":{"type":"integer","title":"Timestamp"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["command","user_id","title","content","timestamp"],"title":"PromptModel"},"PromptSuggestion":{"properties":{"title":{"items":{"type":"string"},"type":"array","title":"Title"},"content":{"type":"string","title":"Content"}},"type":"object","required":["title","content"],"title":"PromptSuggestion"},"PromptUserResponse":{"properties":{"command":{"type":"string","title":"Command"},"user_id":{"type":"string","title":"User Id"},"title":{"type":"string","title":"Title"},"content":{"type":"string","title":"Content"},"timestamp":{"type":"integer","title":"Timestamp"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__models__users__UserResponse"},{"type":"null"}]}},"type":"object","required":["command","user_id","title","content","timestamp"],"title":"PromptUserResponse"},"PushModelForm":{"properties":{"model":{"type":"string","title":"Model"},"insecure":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Insecure"},"stream":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Stream"}},"type":"object","required":["model"],"title":"PushModelForm"},"QueryCollectionsForm":{"properties":{"collection_names":{"items":{"type":"string"},"type":"array","title":"Collection Names"},"query":{"type":"string","title":"Query"},"k":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K"},"k_reranker":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K Reranker"},"r":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"R"},"hybrid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Hybrid"},"hybrid_bm25_weight":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hybrid Bm25 Weight"}},"type":"object","required":["collection_names","query"],"title":"QueryCollectionsForm"},"QueryDocForm":{"properties":{"collection_name":{"type":"string","title":"Collection Name"},"query":{"type":"string","title":"Query"},"k":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K"},"k_reranker":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K Reranker"},"r":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"R"},"hybrid":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Hybrid"}},"type":"object","required":["collection_name","query"],"title":"QueryDocForm"},"QueryMemoryForm":{"properties":{"content":{"type":"string","title":"Content"},"k":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K","default":1}},"type":"object","required":["content"],"title":"QueryMemoryForm"},"RatingData":{"properties":{"rating":{"anyOf":[{"type":"integer"},{"type":"string"},{"type":"null"}],"title":"Rating"},"model_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Id"},"sibling_model_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Sibling Model Ids"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"additionalProperties":true,"type":"object","title":"RatingData"},"ReactionForm":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"ReactionForm"},"Reactions":{"properties":{"name":{"type":"string","title":"Name"},"user_ids":{"items":{"type":"string"},"type":"array","title":"User Ids"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["name","user_ids","count"],"title":"Reactions"},"STTConfigForm":{"properties":{"OPENAI_API_BASE_URL":{"type":"string","title":"Openai Api Base Url"},"OPENAI_API_KEY":{"type":"string","title":"Openai Api Key"},"ENGINE":{"type":"string","title":"Engine"},"MODEL":{"type":"string","title":"Model"},"SUPPORTED_CONTENT_TYPES":{"items":{"type":"string"},"type":"array","title":"Supported Content Types","default":[]},"WHISPER_MODEL":{"type":"string","title":"Whisper Model"},"DEEPGRAM_API_KEY":{"type":"string","title":"Deepgram Api Key"},"AZURE_API_KEY":{"type":"string","title":"Azure Api Key"},"AZURE_REGION":{"type":"string","title":"Azure Region"},"AZURE_LOCALES":{"type":"string","title":"Azure Locales"},"AZURE_BASE_URL":{"type":"string","title":"Azure Base Url"},"AZURE_MAX_SPEAKERS":{"type":"string","title":"Azure Max Speakers"}},"type":"object","required":["OPENAI_API_BASE_URL","OPENAI_API_KEY","ENGINE","MODEL","WHISPER_MODEL","DEEPGRAM_API_KEY","AZURE_API_KEY","AZURE_REGION","AZURE_LOCALES","AZURE_BASE_URL","AZURE_MAX_SPEAKERS"],"title":"STTConfigForm"},"SearchForm":{"properties":{"queries":{"items":{"type":"string"},"type":"array","title":"Queries"}},"type":"object","required":["queries"],"title":"SearchForm"},"SessionUserInfoResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"token":{"type":"string","title":"Token"},"token_type":{"type":"string","title":"Token Type"},"expires_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires At"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"date_of_birth":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Of Birth"}},"type":"object","required":["id","email","name","role","profile_image_url","token","token_type"],"title":"SessionUserInfoResponse"},"SessionUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"token":{"type":"string","title":"Token"},"token_type":{"type":"string","title":"Token Type"},"expires_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expires At"},"permissions":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Permissions"}},"type":"object","required":["id","email","name","role","profile_image_url","token","token_type"],"title":"SessionUserResponse"},"SetBannersForm":{"properties":{"banners":{"items":{"$ref":"#/components/schemas/BannerModel"},"type":"array","title":"Banners"}},"type":"object","required":["banners"],"title":"SetBannersForm"},"SetDefaultSuggestionsForm":{"properties":{"suggestions":{"items":{"$ref":"#/components/schemas/PromptSuggestion"},"type":"array","title":"Suggestions"}},"type":"object","required":["suggestions"],"title":"SetDefaultSuggestionsForm"},"SharingPermissions":{"properties":{"public_models":{"type":"boolean","title":"Public Models","default":true},"public_knowledge":{"type":"boolean","title":"Public Knowledge","default":true},"public_prompts":{"type":"boolean","title":"Public Prompts","default":true},"public_tools":{"type":"boolean","title":"Public Tools","default":true}},"type":"object","title":"SharingPermissions"},"SigninForm":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"SigninForm"},"SigninResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"token":{"type":"string","title":"Token"},"token_type":{"type":"string","title":"Token Type"}},"type":"object","required":["id","email","name","role","profile_image_url","token","token_type"],"title":"SigninResponse"},"SignupForm":{"properties":{"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"},"profile_image_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Profile Image Url","default":"/user.png"}},"type":"object","required":["name","email","password"],"title":"SignupForm"},"SnapshotData":{"properties":{"chat":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Chat"}},"additionalProperties":true,"type":"object","title":"SnapshotData"},"SyncFunctionsForm":{"properties":{"functions":{"items":{"$ref":"#/components/schemas/FunctionWithValvesModel"},"type":"array","title":"Functions","default":[]}},"type":"object","title":"SyncFunctionsForm"},"SyncModelsForm":{"properties":{"models":{"items":{"$ref":"#/components/schemas/ModelModel"},"type":"array","title":"Models","default":[]}},"type":"object","title":"SyncModelsForm"},"TTSConfigForm":{"properties":{"OPENAI_API_BASE_URL":{"type":"string","title":"Openai Api Base Url"},"OPENAI_API_KEY":{"type":"string","title":"Openai Api Key"},"API_KEY":{"type":"string","title":"Api Key"},"ENGINE":{"type":"string","title":"Engine"},"MODEL":{"type":"string","title":"Model"},"VOICE":{"type":"string","title":"Voice"},"SPLIT_ON":{"type":"string","title":"Split On"},"AZURE_SPEECH_REGION":{"type":"string","title":"Azure Speech Region"},"AZURE_SPEECH_BASE_URL":{"type":"string","title":"Azure Speech Base Url"},"AZURE_SPEECH_OUTPUT_FORMAT":{"type":"string","title":"Azure Speech Output Format"}},"type":"object","required":["OPENAI_API_BASE_URL","OPENAI_API_KEY","API_KEY","ENGINE","MODEL","VOICE","SPLIT_ON","AZURE_SPEECH_REGION","AZURE_SPEECH_BASE_URL","AZURE_SPEECH_OUTPUT_FORMAT"],"title":"TTSConfigForm"},"TagFilterForm":{"properties":{"name":{"type":"string","title":"Name"},"skip":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Skip","default":0},"limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Limit","default":50}},"type":"object","required":["name"],"title":"TagFilterForm"},"TagForm":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"TagForm"},"TagModel":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"user_id":{"type":"string","title":"User Id"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"}},"type":"object","required":["id","name","user_id"],"title":"TagModel"},"TaskConfigForm":{"properties":{"TASK_MODEL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Model"},"TASK_MODEL_EXTERNAL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Model External"},"ENABLE_TITLE_GENERATION":{"type":"boolean","title":"Enable Title Generation"},"TITLE_GENERATION_PROMPT_TEMPLATE":{"type":"string","title":"Title Generation Prompt Template"},"IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE":{"type":"string","title":"Image Prompt Generation Prompt Template"},"ENABLE_AUTOCOMPLETE_GENERATION":{"type":"boolean","title":"Enable Autocomplete Generation"},"AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH":{"type":"integer","title":"Autocomplete Generation Input Max Length"},"TAGS_GENERATION_PROMPT_TEMPLATE":{"type":"string","title":"Tags Generation Prompt Template"},"FOLLOW_UP_GENERATION_PROMPT_TEMPLATE":{"type":"string","title":"Follow Up Generation Prompt Template"},"ENABLE_FOLLOW_UP_GENERATION":{"type":"boolean","title":"Enable Follow Up Generation"},"ENABLE_TAGS_GENERATION":{"type":"boolean","title":"Enable Tags Generation"},"ENABLE_SEARCH_QUERY_GENERATION":{"type":"boolean","title":"Enable Search Query Generation"},"ENABLE_RETRIEVAL_QUERY_GENERATION":{"type":"boolean","title":"Enable Retrieval Query Generation"},"QUERY_GENERATION_PROMPT_TEMPLATE":{"type":"string","title":"Query Generation Prompt Template"},"TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE":{"type":"string","title":"Tools Function Calling Prompt Template"}},"type":"object","required":["TASK_MODEL","TASK_MODEL_EXTERNAL","ENABLE_TITLE_GENERATION","TITLE_GENERATION_PROMPT_TEMPLATE","IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE","ENABLE_AUTOCOMPLETE_GENERATION","AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH","TAGS_GENERATION_PROMPT_TEMPLATE","FOLLOW_UP_GENERATION_PROMPT_TEMPLATE","ENABLE_FOLLOW_UP_GENERATION","ENABLE_TAGS_GENERATION","ENABLE_SEARCH_QUERY_GENERATION","ENABLE_RETRIEVAL_QUERY_GENERATION","QUERY_GENERATION_PROMPT_TEMPLATE","TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE"],"title":"TaskConfigForm"},"ToolForm":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"meta":{"$ref":"#/components/schemas/ToolMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"}},"type":"object","required":["id","name","content","meta"],"title":"ToolForm"},"ToolMeta":{"properties":{"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"manifest":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Manifest","default":{}}},"type":"object","title":"ToolMeta"},"ToolModel":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"content":{"type":"string","title":"Content"},"specs":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Specs"},"meta":{"$ref":"#/components/schemas/ToolMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","content","specs","meta","updated_at","created_at"],"title":"ToolModel"},"ToolResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"meta":{"$ref":"#/components/schemas/ToolMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","user_id","name","meta","updated_at","created_at"],"title":"ToolResponse"},"ToolServerConnection":{"properties":{"url":{"type":"string","title":"Url"},"path":{"type":"string","title":"Path"},"auth_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Auth Type"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config"}},"additionalProperties":true,"type":"object","required":["url","path","auth_type","key","config"],"title":"ToolServerConnection"},"ToolServersConfigForm":{"properties":{"TOOL_SERVER_CONNECTIONS":{"items":{"$ref":"#/components/schemas/ToolServerConnection"},"type":"array","title":"Tool Server Connections"}},"type":"object","required":["TOOL_SERVER_CONNECTIONS"],"title":"ToolServersConfigForm"},"ToolUserResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"name":{"type":"string","title":"Name"},"meta":{"$ref":"#/components/schemas/ToolMeta"},"access_control":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Access Control"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"},"user":{"anyOf":[{"$ref":"#/components/schemas/open_webui__models__users__UserResponse"},{"type":"null"}]}},"type":"object","required":["id","user_id","name","meta","updated_at","created_at"],"title":"ToolUserResponse"},"UpdateConfigForm":{"properties":{"ENABLE_EVALUATION_ARENA_MODELS":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Evaluation Arena Models"},"EVALUATION_ARENA_MODELS":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Evaluation Arena Models"}},"type":"object","title":"UpdateConfigForm"},"UpdatePasswordForm":{"properties":{"password":{"type":"string","title":"Password"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["password","new_password"],"title":"UpdatePasswordForm"},"UpdateProfileForm":{"properties":{"profile_image_url":{"type":"string","title":"Profile Image Url"},"name":{"type":"string","title":"Name"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"date_of_birth":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Of Birth"}},"type":"object","required":["profile_image_url","name"],"title":"UpdateProfileForm"},"UrlForm":{"properties":{"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"UrlForm"},"UserIdsForm":{"properties":{"user_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"User Ids"}},"type":"object","title":"UserIdsForm"},"UserInfoListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserInfoResponse"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["users","total"],"title":"UserInfoListResponse"},"UserInfoResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"}},"type":"object","required":["id","name","email","role"],"title":"UserInfoResponse"},"UserListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserModel"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["users","total"],"title":"UserListResponse"},"UserModel":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"username":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Username"},"role":{"type":"string","title":"Role","default":"pending"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"gender":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gender"},"date_of_birth":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Date Of Birth"},"info":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Info"},"settings":{"anyOf":[{"$ref":"#/components/schemas/UserSettings"},{"type":"null"}]},"api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Api Key"},"oauth_sub":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Oauth Sub"},"last_active_at":{"type":"integer","title":"Last Active At"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","name","email","profile_image_url","last_active_at","updated_at","created_at"],"title":"UserModel"},"UserNameResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"}},"type":"object","required":["id","name","role","profile_image_url"],"title":"UserNameResponse"},"UserPermissions":{"properties":{"workspace":{"$ref":"#/components/schemas/WorkspacePermissions"},"sharing":{"$ref":"#/components/schemas/SharingPermissions"},"chat":{"$ref":"#/components/schemas/ChatPermissions"},"features":{"$ref":"#/components/schemas/FeaturesPermissions"}},"type":"object","required":["workspace","sharing","chat","features"],"title":"UserPermissions"},"UserSettings":{"properties":{"ui":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Ui","default":{}}},"additionalProperties":true,"type":"object","title":"UserSettings"},"UserUpdateForm":{"properties":{"role":{"type":"string","title":"Role"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password"}},"type":"object","required":["role","name","email","profile_image_url"],"title":"UserUpdateForm"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebConfig":{"properties":{"ENABLE_WEB_SEARCH":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Web Search"},"WEB_SEARCH_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Web Search Engine"},"WEB_SEARCH_TRUST_ENV":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Web Search Trust Env"},"WEB_SEARCH_RESULT_COUNT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Web Search Result Count"},"WEB_SEARCH_CONCURRENT_REQUESTS":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Web Search Concurrent Requests"},"WEB_LOADER_CONCURRENT_REQUESTS":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Web Loader Concurrent Requests"},"WEB_SEARCH_DOMAIN_FILTER_LIST":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Web Search Domain Filter List","default":[]},"BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Bypass Web Search Embedding And Retrieval"},"BYPASS_WEB_SEARCH_WEB_LOADER":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Bypass Web Search Web Loader"},"SEARXNG_QUERY_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Searxng Query Url"},"YACY_QUERY_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Yacy Query Url"},"YACY_USERNAME":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Yacy Username"},"YACY_PASSWORD":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Yacy Password"},"GOOGLE_PSE_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Pse Api Key"},"GOOGLE_PSE_ENGINE_ID":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Google Pse Engine Id"},"BRAVE_SEARCH_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brave Search Api Key"},"KAGI_SEARCH_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kagi Search Api Key"},"MOJEEK_SEARCH_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mojeek Search Api Key"},"BOCHA_SEARCH_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bocha Search Api Key"},"SERPSTACK_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serpstack Api Key"},"SERPSTACK_HTTPS":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Serpstack Https"},"SERPER_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serper Api Key"},"SERPLY_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serply Api Key"},"TAVILY_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tavily Api Key"},"SEARCHAPI_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Searchapi Api Key"},"SEARCHAPI_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Searchapi Engine"},"SERPAPI_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serpapi Api Key"},"SERPAPI_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Serpapi Engine"},"JINA_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Jina Api Key"},"BING_SEARCH_V7_ENDPOINT":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bing Search V7 Endpoint"},"BING_SEARCH_V7_SUBSCRIPTION_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bing Search V7 Subscription Key"},"EXA_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Exa Api Key"},"PERPLEXITY_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Perplexity Api Key"},"PERPLEXITY_MODEL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Perplexity Model"},"PERPLEXITY_SEARCH_CONTEXT_USAGE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Perplexity Search Context Usage"},"SOUGOU_API_SID":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sougou Api Sid"},"SOUGOU_API_SK":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sougou Api Sk"},"WEB_LOADER_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Web Loader Engine"},"ENABLE_WEB_LOADER_SSL_VERIFICATION":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Web Loader Ssl Verification"},"PLAYWRIGHT_WS_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Playwright Ws Url"},"PLAYWRIGHT_TIMEOUT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Playwright Timeout"},"FIRECRAWL_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Firecrawl Api Key"},"FIRECRAWL_API_BASE_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Firecrawl Api Base Url"},"TAVILY_EXTRACT_DEPTH":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tavily Extract Depth"},"EXTERNAL_WEB_SEARCH_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Web Search Url"},"EXTERNAL_WEB_SEARCH_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Web Search Api Key"},"EXTERNAL_WEB_LOADER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Web Loader Url"},"EXTERNAL_WEB_LOADER_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Web Loader Api Key"},"YOUTUBE_LOADER_LANGUAGE":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Youtube Loader Language"},"YOUTUBE_LOADER_PROXY_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Loader Proxy Url"},"YOUTUBE_LOADER_TRANSLATION":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Youtube Loader Translation"}},"type":"object","title":"WebConfig"},"WorkspacePermissions":{"properties":{"models":{"type":"boolean","title":"Models","default":false},"knowledge":{"type":"boolean","title":"Knowledge","default":false},"prompts":{"type":"boolean","title":"Prompts","default":false},"tools":{"type":"boolean","title":"Tools","default":false}},"type":"object","title":"WorkspacePermissions"},"open_webui__models__auths__UserResponse":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"}},"type":"object","required":["id","email","name","role","profile_image_url"],"title":"UserResponse"},"open_webui__models__messages__MessageForm":{"properties":{"content":{"type":"string","title":"Content"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Data"},"meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta"}},"type":"object","required":["content"],"title":"MessageForm"},"open_webui__models__users__UserResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role"},"profile_image_url":{"type":"string","title":"Profile Image Url"}},"type":"object","required":["id","name","email","role","profile_image_url"],"title":"UserResponse"},"open_webui__routers__chats__MessageForm":{"properties":{"content":{"type":"string","title":"Content"}},"type":"object","required":["content"],"title":"MessageForm"},"open_webui__routers__evaluations__UserResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"role":{"type":"string","title":"Role","default":"pending"},"last_active_at":{"type":"integer","title":"Last Active At"},"updated_at":{"type":"integer","title":"Updated At"},"created_at":{"type":"integer","title":"Created At"}},"type":"object","required":["id","name","email","last_active_at","updated_at","created_at"],"title":"UserResponse"},"open_webui__routers__images__ConfigForm":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"engine":{"type":"string","title":"Engine"},"prompt_generation":{"type":"boolean","title":"Prompt Generation"},"openai":{"$ref":"#/components/schemas/open_webui__routers__images__OpenAIConfigForm"},"automatic1111":{"$ref":"#/components/schemas/Automatic1111ConfigForm"},"comfyui":{"$ref":"#/components/schemas/ComfyUIConfigForm"},"gemini":{"$ref":"#/components/schemas/GeminiConfigForm"}},"type":"object","required":["enabled","engine","prompt_generation","openai","automatic1111","comfyui","gemini"],"title":"ConfigForm"},"open_webui__routers__images__OpenAIConfigForm":{"properties":{"OPENAI_API_BASE_URL":{"type":"string","title":"Openai Api Base Url"},"OPENAI_API_VERSION":{"type":"string","title":"Openai Api Version"},"OPENAI_API_KEY":{"type":"string","title":"Openai Api Key"}},"type":"object","required":["OPENAI_API_BASE_URL","OPENAI_API_VERSION","OPENAI_API_KEY"],"title":"OpenAIConfigForm"},"open_webui__routers__ollama__ConnectionVerificationForm":{"properties":{"url":{"type":"string","title":"Url"},"key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key"}},"type":"object","required":["url"],"title":"ConnectionVerificationForm"},"open_webui__routers__ollama__OllamaConfigForm":{"properties":{"ENABLE_OLLAMA_API":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Ollama Api"},"OLLAMA_BASE_URLS":{"items":{"type":"string"},"type":"array","title":"Ollama Base Urls"},"OLLAMA_API_CONFIGS":{"additionalProperties":true,"type":"object","title":"Ollama Api Configs"}},"type":"object","required":["OLLAMA_BASE_URLS","OLLAMA_API_CONFIGS"],"title":"OllamaConfigForm"},"open_webui__routers__openai__ConnectionVerificationForm":{"properties":{"url":{"type":"string","title":"Url"},"key":{"type":"string","title":"Key"},"config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Config"}},"type":"object","required":["url","key"],"title":"ConnectionVerificationForm"},"open_webui__routers__openai__OpenAIConfigForm":{"properties":{"ENABLE_OPENAI_API":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Openai Api"},"OPENAI_API_BASE_URLS":{"items":{"type":"string"},"type":"array","title":"Openai Api Base Urls"},"OPENAI_API_KEYS":{"items":{"type":"string"},"type":"array","title":"Openai Api Keys"},"OPENAI_API_CONFIGS":{"additionalProperties":true,"type":"object","title":"Openai Api Configs"}},"type":"object","required":["OPENAI_API_BASE_URLS","OPENAI_API_KEYS","OPENAI_API_CONFIGS"],"title":"OpenAIConfigForm"},"open_webui__routers__retrieval__ConfigForm":{"properties":{"RAG_TEMPLATE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag Template"},"TOP_K":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Top K"},"BYPASS_EMBEDDING_AND_RETRIEVAL":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Bypass Embedding And Retrieval"},"RAG_FULL_CONTEXT":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Rag Full Context"},"ENABLE_RAG_HYBRID_SEARCH":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Rag Hybrid Search"},"TOP_K_RERANKER":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Top K Reranker"},"RELEVANCE_THRESHOLD":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Relevance Threshold"},"HYBRID_BM25_WEIGHT":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hybrid Bm25 Weight"},"CONTENT_EXTRACTION_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Extraction Engine"},"PDF_EXTRACT_IMAGES":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Pdf Extract Images"},"DATALAB_MARKER_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datalab Marker Api Key"},"DATALAB_MARKER_API_BASE_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datalab Marker Api Base Url"},"DATALAB_MARKER_ADDITIONAL_CONFIG":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datalab Marker Additional Config"},"DATALAB_MARKER_SKIP_CACHE":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Skip Cache"},"DATALAB_MARKER_FORCE_OCR":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Force Ocr"},"DATALAB_MARKER_PAGINATE":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Paginate"},"DATALAB_MARKER_STRIP_EXISTING_OCR":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Strip Existing Ocr"},"DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Disable Image Extraction"},"DATALAB_MARKER_FORMAT_LINES":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Format Lines"},"DATALAB_MARKER_USE_LLM":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Datalab Marker Use Llm"},"DATALAB_MARKER_OUTPUT_FORMAT":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Datalab Marker Output Format"},"EXTERNAL_DOCUMENT_LOADER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Document Loader Url"},"EXTERNAL_DOCUMENT_LOADER_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Document Loader Api Key"},"TIKA_SERVER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tika Server Url"},"DOCLING_SERVER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Server Url"},"DOCLING_DO_OCR":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Docling Do Ocr"},"DOCLING_FORCE_OCR":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Docling Force Ocr"},"DOCLING_OCR_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Ocr Engine"},"DOCLING_OCR_LANG":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Ocr Lang"},"DOCLING_PDF_BACKEND":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Pdf Backend"},"DOCLING_TABLE_MODE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Table Mode"},"DOCLING_PIPELINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Pipeline"},"DOCLING_DO_PICTURE_DESCRIPTION":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Docling Do Picture Description"},"DOCLING_PICTURE_DESCRIPTION_MODE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docling Picture Description Mode"},"DOCLING_PICTURE_DESCRIPTION_LOCAL":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Docling Picture Description Local"},"DOCLING_PICTURE_DESCRIPTION_API":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Docling Picture Description Api"},"DOCUMENT_INTELLIGENCE_ENDPOINT":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Intelligence Endpoint"},"DOCUMENT_INTELLIGENCE_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Intelligence Key"},"MISTRAL_OCR_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mistral Ocr Api Key"},"RAG_RERANKING_MODEL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag Reranking Model"},"RAG_RERANKING_ENGINE":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag Reranking Engine"},"RAG_EXTERNAL_RERANKER_URL":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag External Reranker Url"},"RAG_EXTERNAL_RERANKER_API_KEY":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rag External Reranker Api Key"},"TEXT_SPLITTER":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text Splitter"},"CHUNK_SIZE":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chunk Size"},"CHUNK_OVERLAP":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chunk Overlap"},"FILE_MAX_SIZE":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Max Size"},"FILE_MAX_COUNT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Max Count"},"FILE_IMAGE_COMPRESSION_WIDTH":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Image Compression Width"},"FILE_IMAGE_COMPRESSION_HEIGHT":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"File Image Compression Height"},"ALLOWED_FILE_EXTENSIONS":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed File Extensions"},"ENABLE_GOOGLE_DRIVE_INTEGRATION":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Google Drive Integration"},"ENABLE_ONEDRIVE_INTEGRATION":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enable Onedrive Integration"},"web":{"anyOf":[{"$ref":"#/components/schemas/WebConfig"},{"type":"null"}]}},"type":"object","title":"ConfigForm"},"open_webui__routers__retrieval__OllamaConfigForm":{"properties":{"url":{"type":"string","title":"Url"},"key":{"type":"string","title":"Key"}},"type":"object","required":["url","key"],"title":"OllamaConfigForm"},"open_webui__routers__retrieval__OpenAIConfigForm":{"properties":{"url":{"type":"string","title":"Url"},"key":{"type":"string","title":"Key"}},"type":"object","required":["url","key"],"title":"OpenAIConfigForm"},"open_webui__routers__users__UserResponse":{"properties":{"name":{"type":"string","title":"Name"},"profile_image_url":{"type":"string","title":"Profile Image Url"},"active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Active"}},"type":"object","required":["name","profile_image_url"],"title":"UserResponse"}},"securitySchemes":{"HTTPBearer":{"type":"http","scheme":"bearer"}}}} \ No newline at end of file +{ + "openapi": "3.1.0", + "info": { + "title": "Open WebUI", + "version": "0.1.0" + }, + "paths": { + "/ollama/": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Status", + "operationId": "get_status_ollama__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + }, + "head": { + "tags": [ + "ollama" + ], + "summary": "Get Status", + "operationId": "get_status_ollama__head", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/ollama/verify": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Verify Connection", + "operationId": "verify_connection_ollama_verify_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__ollama__ConnectionVerificationForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/config": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Config", + "operationId": "get_config_ollama_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/config/update": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Update Config", + "operationId": "update_config_ollama_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__ollama__OllamaConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/api/tags/{url_idx}": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Ollama Tags", + "operationId": "get_ollama_tags_ollama_api_tags__url_idx__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/tags": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Ollama Tags", + "operationId": "get_ollama_tags_ollama_api_tags_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/ps": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Ollama Loaded Models", + "description": "List models that are currently loaded into Ollama memory, and which node they are loaded on.", + "operationId": "get_ollama_loaded_models_ollama_api_ps_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/api/version/{url_idx}": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Ollama Versions", + "operationId": "get_ollama_versions_ollama_api_version__url_idx__get", + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/version": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Ollama Versions", + "operationId": "get_ollama_versions_ollama_api_version_get", + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/unload": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Unload Model", + "operationId": "unload_model_ollama_api_unload_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/api/pull/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Pull Model", + "operationId": "pull_model_ollama_api_pull__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/pull": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Pull Model", + "operationId": "pull_model_ollama_api_pull_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/push/{url_idx}": { + "delete": { + "tags": [ + "ollama" + ], + "summary": "Push Model", + "operationId": "push_model_ollama_api_push__url_idx__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PushModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/push": { + "delete": { + "tags": [ + "ollama" + ], + "summary": "Push Model", + "operationId": "push_model_ollama_api_push_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PushModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/create/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Create Model", + "operationId": "create_model_ollama_api_create__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "type": "integer", + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/create": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Create Model", + "operationId": "create_model_ollama_api_create_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/copy/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Copy Model", + "operationId": "copy_model_ollama_api_copy__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CopyModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/copy": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Copy Model", + "operationId": "copy_model_ollama_api_copy_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CopyModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/delete/{url_idx}": { + "delete": { + "tags": [ + "ollama" + ], + "summary": "Delete Model", + "operationId": "delete_model_ollama_api_delete__url_idx__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/delete": { + "delete": { + "tags": [ + "ollama" + ], + "summary": "Delete Model", + "operationId": "delete_model_ollama_api_delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/show": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Show Model Info", + "operationId": "show_model_info_ollama_api_show_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelNameForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/ollama/api/embed/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Embed", + "operationId": "embed_ollama_api_embed__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateEmbedForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/embed": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Embed", + "operationId": "embed_ollama_api_embed_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateEmbedForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/embeddings/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Embeddings", + "operationId": "embeddings_ollama_api_embeddings__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateEmbeddingsForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/embeddings": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Embeddings", + "operationId": "embeddings_ollama_api_embeddings_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateEmbeddingsForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/generate/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Completion", + "operationId": "generate_completion_ollama_api_generate__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateCompletionForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/generate": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Completion", + "operationId": "generate_completion_ollama_api_generate_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateCompletionForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/chat/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Chat Completion", + "operationId": "generate_chat_completion_ollama_api_chat__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + }, + { + "name": "bypass_filter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "title": "Bypass Filter" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/api/chat": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Chat Completion", + "operationId": "generate_chat_completion_ollama_api_chat_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + }, + { + "name": "bypass_filter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "title": "Bypass Filter" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/completions/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Openai Completion", + "operationId": "generate_openai_completion_ollama_v1_completions__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/completions": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Openai Completion", + "operationId": "generate_openai_completion_ollama_v1_completions_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/chat/completions/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Openai Chat Completion", + "operationId": "generate_openai_chat_completion_ollama_v1_chat_completions__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/chat/completions": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Generate Openai Chat Completion", + "operationId": "generate_openai_chat_completion_ollama_v1_chat_completions_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/models/{url_idx}": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Openai Models", + "operationId": "get_openai_models_ollama_v1_models__url_idx__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/v1/models": { + "get": { + "tags": [ + "ollama" + ], + "summary": "Get Openai Models", + "operationId": "get_openai_models_ollama_v1_models_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/models/download/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Download Model", + "operationId": "download_model_ollama_models_download__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UrlForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/models/download": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Download Model", + "operationId": "download_model_ollama_models_download_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UrlForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/models/upload/{url_idx}": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Upload Model", + "operationId": "upload_model_ollama_models_upload__url_idx__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_model_ollama_models_upload__url_idx__post" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/ollama/models/upload": { + "post": { + "tags": [ + "ollama" + ], + "summary": "Upload Model", + "operationId": "upload_model_ollama_models_upload_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_model_ollama_models_upload_post" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/openai/config": { + "get": { + "tags": [ + "openai" + ], + "summary": "Get Config", + "operationId": "get_config_openai_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/openai/config/update": { + "post": { + "tags": [ + "openai" + ], + "summary": "Update Config", + "operationId": "update_config_openai_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__openai__OpenAIConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/openai/audio/speech": { + "post": { + "tags": [ + "openai" + ], + "summary": "Speech", + "operationId": "speech_openai_audio_speech_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/openai/models/{url_idx}": { + "get": { + "tags": [ + "openai" + ], + "summary": "Get Models", + "operationId": "get_models_openai_models__url_idx__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/openai/models": { + "get": { + "tags": [ + "openai" + ], + "summary": "Get Models", + "operationId": "get_models_openai_models_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "url_idx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Url Idx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/openai/verify": { + "post": { + "tags": [ + "openai" + ], + "summary": "Verify Connection", + "operationId": "verify_connection_openai_verify_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__openai__ConnectionVerificationForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/openai/chat/completions": { + "post": { + "tags": [ + "openai" + ], + "summary": "Generate Chat Completion", + "operationId": "generate_chat_completion_openai_chat_completions_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "bypass_filter", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": false, + "title": "Bypass Filter" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/openai/{path}": { + "delete": { + "tags": [ + "openai" + ], + "summary": "Proxy", + "description": "Deprecated: proxy all requests to OpenAI API", + "operationId": "proxy_openai__path__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "openai" + ], + "summary": "Proxy", + "description": "Deprecated: proxy all requests to OpenAI API", + "operationId": "proxy_openai__path__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "put": { + "tags": [ + "openai" + ], + "summary": "Proxy", + "description": "Deprecated: proxy all requests to OpenAI API", + "operationId": "proxy_openai__path__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "openai" + ], + "summary": "Proxy", + "description": "Deprecated: proxy all requests to OpenAI API", + "operationId": "proxy_openai__path__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/pipelines/list": { + "get": { + "tags": [ + "pipelines" + ], + "summary": "Get Pipelines List", + "operationId": "get_pipelines_list_api_v1_pipelines_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/pipelines/upload": { + "post": { + "tags": [ + "pipelines" + ], + "summary": "Upload Pipeline", + "operationId": "upload_pipeline_api_v1_pipelines_upload_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_pipeline_api_v1_pipelines_upload_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/pipelines/add": { + "post": { + "tags": [ + "pipelines" + ], + "summary": "Add Pipeline", + "operationId": "add_pipeline_api_v1_pipelines_add_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddPipelineForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/pipelines/delete": { + "delete": { + "tags": [ + "pipelines" + ], + "summary": "Delete Pipeline", + "operationId": "delete_pipeline_api_v1_pipelines_delete_delete", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeletePipelineForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/pipelines/": { + "get": { + "tags": [ + "pipelines" + ], + "summary": "Get Pipelines", + "operationId": "get_pipelines_api_v1_pipelines__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "urlIdx", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Urlidx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/pipelines/{pipeline_id}/valves": { + "get": { + "tags": [ + "pipelines" + ], + "summary": "Get Pipeline Valves", + "operationId": "get_pipeline_valves_api_v1_pipelines__pipeline_id__valves_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "pipeline_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Pipeline Id" + } + }, + { + "name": "urlIdx", + "in": "query", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Urlidx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/pipelines/{pipeline_id}/valves/spec": { + "get": { + "tags": [ + "pipelines" + ], + "summary": "Get Pipeline Valves Spec", + "operationId": "get_pipeline_valves_spec_api_v1_pipelines__pipeline_id__valves_spec_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "pipeline_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Pipeline Id" + } + }, + { + "name": "urlIdx", + "in": "query", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Urlidx" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/pipelines/{pipeline_id}/valves/update": { + "post": { + "tags": [ + "pipelines" + ], + "summary": "Update Pipeline Valves", + "operationId": "update_pipeline_valves_api_v1_pipelines__pipeline_id__valves_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "pipeline_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Pipeline Id" + } + }, + { + "name": "urlIdx", + "in": "query", + "required": true, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Urlidx" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tasks/config": { + "get": { + "tags": [ + "tasks" + ], + "summary": "Get Task Config", + "operationId": "get_task_config_api_v1_tasks_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/config/update": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Update Task Config", + "operationId": "update_task_config_api_v1_tasks_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TaskConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/title/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Title", + "operationId": "generate_title_api_v1_tasks_title_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/follow_up/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Follow Ups", + "operationId": "generate_follow_ups_api_v1_tasks_follow_up_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/tags/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Chat Tags", + "operationId": "generate_chat_tags_api_v1_tasks_tags_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/image_prompt/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Image Prompt", + "operationId": "generate_image_prompt_api_v1_tasks_image_prompt_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/queries/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Queries", + "operationId": "generate_queries_api_v1_tasks_queries_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/auto/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Autocompletion", + "operationId": "generate_autocompletion_api_v1_tasks_auto_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/emoji/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Emoji", + "operationId": "generate_emoji_api_v1_tasks_emoji_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tasks/moa/completions": { + "post": { + "tags": [ + "tasks" + ], + "summary": "Generate Moa Response", + "operationId": "generate_moa_response_api_v1_tasks_moa_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/config": { + "get": { + "tags": [ + "images" + ], + "summary": "Get Config", + "operationId": "get_config_api_v1_images_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/config/update": { + "post": { + "tags": [ + "images" + ], + "summary": "Update Config", + "operationId": "update_config_api_v1_images_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__images__ConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/config/url/verify": { + "get": { + "tags": [ + "images" + ], + "summary": "Verify Url", + "operationId": "verify_url_api_v1_images_config_url_verify_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/image/config": { + "get": { + "tags": [ + "images" + ], + "summary": "Get Image Config", + "operationId": "get_image_config_api_v1_images_image_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/image/config/update": { + "post": { + "tags": [ + "images" + ], + "summary": "Update Image Config", + "operationId": "update_image_config_api_v1_images_image_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImageConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/models": { + "get": { + "tags": [ + "images" + ], + "summary": "Get Models", + "operationId": "get_models_api_v1_images_models_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/images/generations": { + "post": { + "tags": [ + "images" + ], + "summary": "Image Generations", + "operationId": "image_generations_api_v1_images_generations_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GenerateImageForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/config": { + "get": { + "tags": [ + "audio" + ], + "summary": "Get Audio Config", + "operationId": "get_audio_config_api_v1_audio_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/config/update": { + "post": { + "tags": [ + "audio" + ], + "summary": "Update Audio Config", + "operationId": "update_audio_config_api_v1_audio_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AudioConfigUpdateForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/speech": { + "post": { + "tags": [ + "audio" + ], + "summary": "Speech", + "operationId": "speech_api_v1_audio_speech_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/transcriptions": { + "post": { + "tags": [ + "audio" + ], + "summary": "Transcription", + "operationId": "transcription_api_v1_audio_transcriptions_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_transcription_api_v1_audio_transcriptions_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/models": { + "get": { + "tags": [ + "audio" + ], + "summary": "Get Models", + "operationId": "get_models_api_v1_audio_models_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/audio/voices": { + "get": { + "tags": [ + "audio" + ], + "summary": "Get Voices", + "operationId": "get_voices_api_v1_audio_voices_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/": { + "get": { + "tags": [ + "retrieval" + ], + "summary": "Get Status", + "operationId": "get_status_api_v1_retrieval__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/v1/retrieval/embedding": { + "get": { + "tags": [ + "retrieval" + ], + "summary": "Get Embedding Config", + "operationId": "get_embedding_config_api_v1_retrieval_embedding_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/embedding/update": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Update Embedding Config", + "operationId": "update_embedding_config_api_v1_retrieval_embedding_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmbeddingModelUpdateForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/config": { + "get": { + "tags": [ + "retrieval" + ], + "summary": "Get Rag Config", + "operationId": "get_rag_config_api_v1_retrieval_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/config/update": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Update Rag Config", + "operationId": "update_rag_config_api_v1_retrieval_config_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__retrieval__ConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/process/file": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process File", + "operationId": "process_file_api_v1_retrieval_process_file_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessFileForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/process/text": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process Text", + "operationId": "process_text_api_v1_retrieval_process_text_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessTextForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/process/youtube": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process Youtube Video", + "operationId": "process_youtube_video_api_v1_retrieval_process_youtube_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessUrlForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/process/web": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process Web", + "operationId": "process_web_api_v1_retrieval_process_web_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ProcessUrlForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/process/web/search": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process Web Search", + "operationId": "process_web_search_api_v1_retrieval_process_web_search_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SearchForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/query/doc": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Query Doc Handler", + "operationId": "query_doc_handler_api_v1_retrieval_query_doc_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryDocForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/query/collection": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Query Collection Handler", + "operationId": "query_collection_handler_api_v1_retrieval_query_collection_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryCollectionsForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/delete": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Delete Entries From Collection", + "operationId": "delete_entries_from_collection_api_v1_retrieval_delete_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DeleteForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/reset/db": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Reset Vector Db", + "operationId": "reset_vector_db_api_v1_retrieval_reset_db_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/reset/uploads": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Reset Upload Dir", + "operationId": "reset_upload_dir_api_v1_retrieval_reset_uploads_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Reset Upload Dir Api V1 Retrieval Reset Uploads Post" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/retrieval/ef/{text}": { + "get": { + "tags": [ + "retrieval" + ], + "summary": "Get Embeddings", + "operationId": "get_embeddings_api_v1_retrieval_ef__text__get", + "parameters": [ + { + "name": "text", + "in": "path", + "required": true, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Text" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/retrieval/process/files/batch": { + "post": { + "tags": [ + "retrieval" + ], + "summary": "Process Files Batch", + "description": "Process a batch of files and save them to the vector database.", + "operationId": "process_files_batch_api_v1_retrieval_process_files_batch_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BatchProcessFilesForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BatchProcessFilesResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/import": { + "post": { + "tags": [ + "configs" + ], + "summary": "Import Config", + "operationId": "import_config_api_v1_configs_import_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ImportConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Import Config Api V1 Configs Import Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/export": { + "get": { + "tags": [ + "configs" + ], + "summary": "Export Config", + "operationId": "export_config_api_v1_configs_export_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Response Export Config Api V1 Configs Export Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/connections": { + "get": { + "tags": [ + "configs" + ], + "summary": "Get Connections Config", + "operationId": "get_connections_config_api_v1_configs_connections_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConnectionsConfigForm" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "configs" + ], + "summary": "Set Connections Config", + "operationId": "set_connections_config_api_v1_configs_connections_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConnectionsConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ConnectionsConfigForm" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/tool_servers": { + "get": { + "tags": [ + "configs" + ], + "summary": "Get Tool Servers Config", + "operationId": "get_tool_servers_config_api_v1_configs_tool_servers_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolServersConfigForm" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "configs" + ], + "summary": "Set Tool Servers Config", + "operationId": "set_tool_servers_config_api_v1_configs_tool_servers_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolServersConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolServersConfigForm" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/tool_servers/verify": { + "post": { + "tags": [ + "configs" + ], + "summary": "Verify Tool Servers Config", + "description": "Verify the connection to the tool server.", + "operationId": "verify_tool_servers_config_api_v1_configs_tool_servers_verify_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolServerConnection" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/code_execution": { + "get": { + "tags": [ + "configs" + ], + "summary": "Get Code Execution Config", + "operationId": "get_code_execution_config_api_v1_configs_code_execution_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CodeInterpreterConfigForm" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "configs" + ], + "summary": "Set Code Execution Config", + "operationId": "set_code_execution_config_api_v1_configs_code_execution_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CodeInterpreterConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CodeInterpreterConfigForm" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/models": { + "get": { + "tags": [ + "configs" + ], + "summary": "Get Models Config", + "operationId": "get_models_config_api_v1_configs_models_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsConfigForm" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "configs" + ], + "summary": "Set Models Config", + "operationId": "set_models_config_api_v1_configs_models_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelsConfigForm" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/suggestions": { + "post": { + "tags": [ + "configs" + ], + "summary": "Set Default Suggestions", + "operationId": "set_default_suggestions_api_v1_configs_suggestions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetDefaultSuggestionsForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PromptSuggestion" + }, + "type": "array", + "title": "Response Set Default Suggestions Api V1 Configs Suggestions Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/configs/banners": { + "get": { + "tags": [ + "configs" + ], + "summary": "Get Banners", + "operationId": "get_banners_api_v1_configs_banners_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/BannerModel" + }, + "type": "array", + "title": "Response Get Banners Api V1 Configs Banners Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "configs" + ], + "summary": "Set Banners", + "operationId": "set_banners_api_v1_configs_banners_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetBannersForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/BannerModel" + }, + "type": "array", + "title": "Response Set Banners Api V1 Configs Banners Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Session User", + "operationId": "get_session_user_api_v1_auths__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionUserInfoResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/update/profile": { + "post": { + "tags": [ + "auths" + ], + "summary": "Update Profile", + "operationId": "update_profile_api_v1_auths_update_profile_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProfileForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__models__auths__UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/update/password": { + "post": { + "tags": [ + "auths" + ], + "summary": "Update Password", + "operationId": "update_password_api_v1_auths_update_password_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePasswordForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Update Password Api V1 Auths Update Password Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/ldap": { + "post": { + "tags": [ + "auths" + ], + "summary": "Ldap Auth", + "operationId": "ldap_auth_api_v1_auths_ldap_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LdapForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionUserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/auths/signin": { + "post": { + "tags": [ + "auths" + ], + "summary": "Signin", + "operationId": "signin_api_v1_auths_signin_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SigninForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionUserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/auths/signup": { + "post": { + "tags": [ + "auths" + ], + "summary": "Signup", + "operationId": "signup_api_v1_auths_signup_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SignupForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SessionUserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/auths/signout": { + "get": { + "tags": [ + "auths" + ], + "summary": "Signout", + "operationId": "signout_api_v1_auths_signout_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/v1/auths/add": { + "post": { + "tags": [ + "auths" + ], + "summary": "Add User", + "operationId": "add_user_api_v1_auths_add_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddUserForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SigninResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/admin/details": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Admin Details", + "operationId": "get_admin_details_api_v1_auths_admin_details_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/admin/config": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Admin Config", + "operationId": "get_admin_config_api_v1_auths_admin_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "auths" + ], + "summary": "Update Admin Config", + "operationId": "update_admin_config_api_v1_auths_admin_config_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AdminConfig" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/admin/config/ldap/server": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Ldap Server", + "operationId": "get_ldap_server_api_v1_auths_admin_config_ldap_server_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LdapServerConfig" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "auths" + ], + "summary": "Update Ldap Server", + "operationId": "update_ldap_server_api_v1_auths_admin_config_ldap_server_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LdapServerConfig" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/admin/config/ldap": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Ldap Config", + "operationId": "get_ldap_config_api_v1_auths_admin_config_ldap_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "auths" + ], + "summary": "Update Ldap Config", + "operationId": "update_ldap_config_api_v1_auths_admin_config_ldap_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LdapConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/auths/api_key": { + "get": { + "tags": [ + "auths" + ], + "summary": "Get Api Key", + "operationId": "get_api_key_api_v1_auths_api_key_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKey" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "auths" + ], + "summary": "Generate Api Key", + "operationId": "generate_api_key_api_v1_auths_api_key_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiKey" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "delete": { + "tags": [ + "auths" + ], + "summary": "Delete Api Key", + "operationId": "delete_api_key_api_v1_auths_api_key_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Api Key Api V1 Auths Api Key Delete" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/active": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Active Users", + "description": "Get a list of active users.", + "operationId": "get_active_users_api_v1_users_active_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Users", + "operationId": "get_users_api_v1_users__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Query" + } + }, + { + "name": "order_by", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Order By" + } + }, + { + "name": "direction", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Direction" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "default": 1, + "title": "Page" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserListResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/all": { + "get": { + "tags": [ + "users" + ], + "summary": "Get All Users", + "operationId": "get_all_users_api_v1_users_all_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserInfoListResponse" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/groups": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Groups", + "operationId": "get_user_groups_api_v1_users_groups_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/permissions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Permissisions", + "operationId": "get_user_permissisions_api_v1_users_permissions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/default/permissions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get Default User Permissions", + "operationId": "get_default_user_permissions_api_v1_users_default_permissions_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPermissions" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "users" + ], + "summary": "Update Default User Permissions", + "operationId": "update_default_user_permissions_api_v1_users_default_permissions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPermissions" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/user/settings": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Settings By Session User", + "operationId": "get_user_settings_by_session_user_api_v1_users_user_settings_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/UserSettings" + }, + { + "type": "null" + } + ], + "title": "Response Get User Settings By Session User Api V1 Users User Settings Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/user/settings/update": { + "post": { + "tags": [ + "users" + ], + "summary": "Update User Settings By Session User", + "operationId": "update_user_settings_by_session_user_api_v1_users_user_settings_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSettings" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserSettings" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/user/info": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Info By Session User", + "operationId": "get_user_info_by_session_user_api_v1_users_user_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Response Get User Info By Session User Api V1 Users User Info Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/user/info/update": { + "post": { + "tags": [ + "users" + ], + "summary": "Update User Info By Session User", + "operationId": "update_user_info_by_session_user_api_v1_users_user_info_update_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Response Update User Info By Session User Api V1 Users User Info Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/users/{user_id}": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User By Id", + "operationId": "get_user_by_id_api_v1_users__user_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__users__UserResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "users" + ], + "summary": "Delete User By Id", + "operationId": "delete_user_by_id_api_v1_users__user_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete User By Id Api V1 Users User Id Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/{user_id}/oauth/sessions": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Oauth Sessions By Id", + "operationId": "get_user_oauth_sessions_by_id_api_v1_users__user_id__oauth_sessions_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get User Oauth Sessions By Id Api V1 Users User Id Oauth Sessions Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/{user_id}/profile/image": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Profile Image By Id", + "operationId": "get_user_profile_image_by_id_api_v1_users__user_id__profile_image_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/{user_id}/active": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Active Status By Id", + "operationId": "get_user_active_status_by_id_api_v1_users__user_id__active_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Response Get User Active Status By Id Api V1 Users User Id Active Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/{user_id}/update": { + "post": { + "tags": [ + "users" + ], + "summary": "Update User By Id", + "operationId": "update_user_by_id_api_v1_users__user_id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserUpdateForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/UserModel" + }, + { + "type": "null" + } + ], + "title": "Response Update User By Id Api V1 Users User Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/users/{user_id}/groups": { + "get": { + "tags": [ + "users" + ], + "summary": "Get User Groups By Id", + "operationId": "get_user_groups_by_id_api_v1_users__user_id__groups_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get Channels", + "operationId": "get_channels_api_v1_channels__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChannelModel" + }, + "type": "array", + "title": "Response Get Channels Api V1 Channels Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/channels/list": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get All Channels", + "operationId": "get_all_channels_api_v1_channels_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChannelModel" + }, + "type": "array", + "title": "Response Get All Channels Api V1 Channels List Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/channels/create": { + "post": { + "tags": [ + "channels" + ], + "summary": "Create New Channel", + "operationId": "create_new_channel_api_v1_channels_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChannelModel" + }, + { + "type": "null" + } + ], + "title": "Response Create New Channel Api V1 Channels Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/channels/{id}": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get Channel By Id", + "operationId": "get_channel_by_id_api_v1_channels__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChannelModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Channel By Id Api V1 Channels Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/update": { + "post": { + "tags": [ + "channels" + ], + "summary": "Update Channel By Id", + "operationId": "update_channel_by_id_api_v1_channels__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChannelModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Channel By Id Api V1 Channels Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/delete": { + "delete": { + "tags": [ + "channels" + ], + "summary": "Delete Channel By Id", + "operationId": "delete_channel_by_id_api_v1_channels__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Channel By Id Api V1 Channels Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get Channel Messages", + "operationId": "get_channel_messages_api_v1_channels__id__messages_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessageUserResponse" + }, + "title": "Response Get Channel Messages Api V1 Channels Id Messages Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/post": { + "post": { + "tags": [ + "channels" + ], + "summary": "Post New Message", + "operationId": "post_new_message_api_v1_channels__id__messages_post_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__models__messages__MessageForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageModel" + }, + { + "type": "null" + } + ], + "title": "Response Post New Message Api V1 Channels Id Messages Post Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get Channel Message", + "operationId": "get_channel_message_api_v1_channels__id__messages__message_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageUserResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Channel Message Api V1 Channels Id Messages Message Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}/thread": { + "get": { + "tags": [ + "channels" + ], + "summary": "Get Channel Thread Messages", + "operationId": "get_channel_thread_messages_api_v1_channels__id__messages__message_id__thread_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + }, + { + "name": "skip", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0, + "title": "Skip" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 50, + "title": "Limit" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MessageUserResponse" + }, + "title": "Response Get Channel Thread Messages Api V1 Channels Id Messages Message Id Thread Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}/update": { + "post": { + "tags": [ + "channels" + ], + "summary": "Update Message By Id", + "operationId": "update_message_by_id_api_v1_channels__id__messages__message_id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__models__messages__MessageForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MessageModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Message By Id Api V1 Channels Id Messages Message Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}/reactions/add": { + "post": { + "tags": [ + "channels" + ], + "summary": "Add Reaction To Message", + "operationId": "add_reaction_to_message_api_v1_channels__id__messages__message_id__reactions_add_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReactionForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Add Reaction To Message Api V1 Channels Id Messages Message Id Reactions Add Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}/reactions/remove": { + "post": { + "tags": [ + "channels" + ], + "summary": "Remove Reaction By Id And User Id And Name", + "operationId": "remove_reaction_by_id_and_user_id_and_name_api_v1_channels__id__messages__message_id__reactions_remove_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReactionForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Remove Reaction By Id And User Id And Name Api V1 Channels Id Messages Message Id Reactions Remove Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/channels/{id}/messages/{message_id}/delete": { + "delete": { + "tags": [ + "channels" + ], + "summary": "Delete Message By Id", + "operationId": "delete_message_by_id_api_v1_channels__id__messages__message_id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Message By Id Api V1 Channels Id Messages Message Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/list": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Session User Chat List", + "operationId": "get_session_user_chat_list_api_v1_chats_list_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Page" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "title": "Response Get Session User Chat List Api V1 Chats List Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Session User Chat List", + "operationId": "get_session_user_chat_list_api_v1_chats__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Page" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "title": "Response Get Session User Chat List Api V1 Chats Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "chats" + ], + "summary": "Delete All User Chats", + "operationId": "delete_all_user_chats_api_v1_chats__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete All User Chats Api V1 Chats Delete" + } + } + } + } + } + } + }, + "/api/v1/chats/list/user/{user_id}": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get User Chat List By User Id", + "operationId": "get_user_chat_list_by_user_id_api_v1_chats_list_user__user_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "User Id" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Page" + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Query" + } + }, + { + "name": "order_by", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Order By" + } + }, + { + "name": "direction", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Direction" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "title": "Response Get User Chat List By User Id Api V1 Chats List User User Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/new": { + "post": { + "tags": [ + "chats" + ], + "summary": "Create New Chat", + "operationId": "create_new_chat_api_v1_chats_new_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Create New Chat Api V1 Chats New Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/import": { + "post": { + "tags": [ + "chats" + ], + "summary": "Import Chat", + "operationId": "import_chat_api_v1_chats_import_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatImportForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Import Chat Api V1 Chats Import Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/search": { + "get": { + "tags": [ + "chats" + ], + "summary": "Search User Chats", + "operationId": "search_user_chats_api_v1_chats_search_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "text", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Text" + } + }, + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Page" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "title": "Response Search User Chats Api V1 Chats Search Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/folder/{folder_id}": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Chats By Folder Id", + "operationId": "get_chats_by_folder_id_api_v1_chats_folder__folder_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "folder_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Folder Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatResponse" + }, + "title": "Response Get Chats By Folder Id Api V1 Chats Folder Folder Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/pinned": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get User Pinned Chats", + "operationId": "get_user_pinned_chats_api_v1_chats_pinned_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "type": "array", + "title": "Response Get User Pinned Chats Api V1 Chats Pinned Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/all": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get User Chats", + "operationId": "get_user_chats_api_v1_chats_all_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatResponse" + }, + "type": "array", + "title": "Response Get User Chats Api V1 Chats All Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/all/archived": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get User Archived Chats", + "operationId": "get_user_archived_chats_api_v1_chats_all_archived_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatResponse" + }, + "type": "array", + "title": "Response Get User Archived Chats Api V1 Chats All Archived Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/all/tags": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get All User Tags", + "operationId": "get_all_user_tags_api_v1_chats_all_tags_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/TagModel" + }, + "type": "array", + "title": "Response Get All User Tags Api V1 Chats All Tags Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/all/db": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get All User Chats In Db", + "operationId": "get_all_user_chats_in_db_api_v1_chats_all_db_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatResponse" + }, + "type": "array", + "title": "Response Get All User Chats In Db Api V1 Chats All Db Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/archived": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Archived Session User Chat List", + "operationId": "get_archived_session_user_chat_list_api_v1_chats_archived_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Page" + } + }, + { + "name": "query", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Query" + } + }, + { + "name": "order_by", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Order By" + } + }, + { + "name": "direction", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Direction" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "title": "Response Get Archived Session User Chat List Api V1 Chats Archived Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/archive/all": { + "post": { + "tags": [ + "chats" + ], + "summary": "Archive All Chats", + "operationId": "archive_all_chats_api_v1_chats_archive_all_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Archive All Chats Api V1 Chats Archive All Post" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/share/{share_id}": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Shared Chat By Id", + "operationId": "get_shared_chat_by_id_api_v1_chats_share__share_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "share_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Share Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Shared Chat By Id Api V1 Chats Share Share Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/tags": { + "post": { + "tags": [ + "chats" + ], + "summary": "Get User Chat List By Tag Name", + "operationId": "get_user_chat_list_by_tag_name_api_v1_chats_tags_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagFilterForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ChatTitleIdResponse" + }, + "type": "array", + "title": "Response Get User Chat List By Tag Name Api V1 Chats Tags Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chats/{id}": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Chat By Id", + "operationId": "get_chat_by_id_api_v1_chats__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Chat By Id Api V1 Chats Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "chats" + ], + "summary": "Update Chat By Id", + "operationId": "update_chat_by_id_api_v1_chats__id__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update Chat By Id Api V1 Chats Id Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "chats" + ], + "summary": "Delete Chat By Id", + "operationId": "delete_chat_by_id_api_v1_chats__id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Chat By Id Api V1 Chats Id Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/messages/{message_id}": { + "post": { + "tags": [ + "chats" + ], + "summary": "Update Chat Message By Id", + "operationId": "update_chat_message_by_id_api_v1_chats__id__messages__message_id__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/open_webui__routers__chats__MessageForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update Chat Message By Id Api V1 Chats Id Messages Message Id Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/messages/{message_id}/event": { + "post": { + "tags": [ + "chats" + ], + "summary": "Send Chat Message Event By Id", + "operationId": "send_chat_message_event_by_id_api_v1_chats__id__messages__message_id__event_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "message_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Message Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EventForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Response Send Chat Message Event By Id Api V1 Chats Id Messages Message Id Event Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/pinned": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Pinned Status By Id", + "operationId": "get_pinned_status_by_id_api_v1_chats__id__pinned_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Response Get Pinned Status By Id Api V1 Chats Id Pinned Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/pin": { + "post": { + "tags": [ + "chats" + ], + "summary": "Pin Chat By Id", + "operationId": "pin_chat_by_id_api_v1_chats__id__pin_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Pin Chat By Id Api V1 Chats Id Pin Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/clone": { + "post": { + "tags": [ + "chats" + ], + "summary": "Clone Chat By Id", + "operationId": "clone_chat_by_id_api_v1_chats__id__clone_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloneForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Clone Chat By Id Api V1 Chats Id Clone Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/clone/shared": { + "post": { + "tags": [ + "chats" + ], + "summary": "Clone Shared Chat By Id", + "operationId": "clone_shared_chat_by_id_api_v1_chats__id__clone_shared_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Clone Shared Chat By Id Api V1 Chats Id Clone Shared Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/archive": { + "post": { + "tags": [ + "chats" + ], + "summary": "Archive Chat By Id", + "operationId": "archive_chat_by_id_api_v1_chats__id__archive_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Archive Chat By Id Api V1 Chats Id Archive Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/share": { + "post": { + "tags": [ + "chats" + ], + "summary": "Share Chat By Id", + "operationId": "share_chat_by_id_api_v1_chats__id__share_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Share Chat By Id Api V1 Chats Id Share Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "chats" + ], + "summary": "Delete Shared Chat By Id", + "operationId": "delete_shared_chat_by_id_api_v1_chats__id__share_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Response Delete Shared Chat By Id Api V1 Chats Id Share Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/folder": { + "post": { + "tags": [ + "chats" + ], + "summary": "Update Chat Folder Id By Id", + "operationId": "update_chat_folder_id_by_id_api_v1_chats__id__folder_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatFolderIdForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ChatResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update Chat Folder Id By Id Api V1 Chats Id Folder Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/tags": { + "get": { + "tags": [ + "chats" + ], + "summary": "Get Chat Tags By Id", + "operationId": "get_chat_tags_by_id_api_v1_chats__id__tags_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TagModel" + }, + "title": "Response Get Chat Tags By Id Api V1 Chats Id Tags Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "chats" + ], + "summary": "Add Tag By Id And Tag Name", + "operationId": "add_tag_by_id_and_tag_name_api_v1_chats__id__tags_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TagModel" + }, + "title": "Response Add Tag By Id And Tag Name Api V1 Chats Id Tags Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "chats" + ], + "summary": "Delete Tag By Id And Tag Name", + "operationId": "delete_tag_by_id_and_tag_name_api_v1_chats__id__tags_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TagForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TagModel" + }, + "title": "Response Delete Tag By Id And Tag Name Api V1 Chats Id Tags Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/chats/{id}/tags/all": { + "delete": { + "tags": [ + "chats" + ], + "summary": "Delete All Tags By Id", + "operationId": "delete_all_tags_by_id_api_v1_chats__id__tags_all_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Response Delete All Tags By Id Api V1 Chats Id Tags All Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/notes/": { + "get": { + "tags": [ + "notes" + ], + "summary": "Get Notes", + "operationId": "get_notes_api_v1_notes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/NoteUserResponse" + }, + "type": "array", + "title": "Response Get Notes Api V1 Notes Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/notes/list": { + "get": { + "tags": [ + "notes" + ], + "summary": "Get Note List", + "operationId": "get_note_list_api_v1_notes_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/NoteTitleIdResponse" + }, + "type": "array", + "title": "Response Get Note List Api V1 Notes List Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/notes/create": { + "post": { + "tags": [ + "notes" + ], + "summary": "Create New Note", + "operationId": "create_new_note_api_v1_notes_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoteForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/NoteModel" + }, + { + "type": "null" + } + ], + "title": "Response Create New Note Api V1 Notes Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/notes/{id}": { + "get": { + "tags": [ + "notes" + ], + "summary": "Get Note By Id", + "operationId": "get_note_by_id_api_v1_notes__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/NoteModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Note By Id Api V1 Notes Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/notes/{id}/update": { + "post": { + "tags": [ + "notes" + ], + "summary": "Update Note By Id", + "operationId": "update_note_by_id_api_v1_notes__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NoteForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/NoteModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Note By Id Api V1 Notes Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/notes/{id}/delete": { + "delete": { + "tags": [ + "notes" + ], + "summary": "Delete Note By Id", + "operationId": "delete_note_by_id_api_v1_notes__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Note By Id Api V1 Notes Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/": { + "get": { + "tags": [ + "models" + ], + "summary": "Get Models", + "operationId": "get_models_api_v1_models__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ModelUserResponse" + }, + "title": "Response Get Models Api V1 Models Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/base": { + "get": { + "tags": [ + "models" + ], + "summary": "Get Base Models", + "operationId": "get_base_models_api_v1_models_base_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ModelResponse" + }, + "type": "array", + "title": "Response Get Base Models Api V1 Models Base Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/models/create": { + "post": { + "tags": [ + "models" + ], + "summary": "Create New Model", + "operationId": "create_new_model_api_v1_models_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelModel" + }, + { + "type": "null" + } + ], + "title": "Response Create New Model Api V1 Models Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/models/export": { + "get": { + "tags": [ + "models" + ], + "summary": "Export Models", + "operationId": "export_models_api_v1_models_export_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ModelModel" + }, + "type": "array", + "title": "Response Export Models Api V1 Models Export Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/models/sync": { + "post": { + "tags": [ + "models" + ], + "summary": "Sync Models", + "operationId": "sync_models_api_v1_models_sync_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SyncModelsForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ModelModel" + }, + "type": "array", + "title": "Response Sync Models Api V1 Models Sync Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/models/model": { + "get": { + "tags": [ + "models" + ], + "summary": "Get Model By Id", + "operationId": "get_model_by_id_api_v1_models_model_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Model By Id Api V1 Models Model Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/model/toggle": { + "post": { + "tags": [ + "models" + ], + "summary": "Toggle Model By Id", + "operationId": "toggle_model_by_id_api_v1_models_model_toggle_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelResponse" + }, + { + "type": "null" + } + ], + "title": "Response Toggle Model By Id Api V1 Models Model Toggle Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/model/update": { + "post": { + "tags": [ + "models" + ], + "summary": "Update Model By Id", + "operationId": "update_model_by_id_api_v1_models_model_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ModelForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ModelModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Model By Id Api V1 Models Model Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/model/delete": { + "delete": { + "tags": [ + "models" + ], + "summary": "Delete Model By Id", + "operationId": "delete_model_by_id_api_v1_models_model_delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Model By Id Api V1 Models Model Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/models/delete/all": { + "delete": { + "tags": [ + "models" + ], + "summary": "Delete All Models", + "operationId": "delete_all_models_api_v1_models_delete_all_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete All Models Api V1 Models Delete All Delete" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/knowledge/": { + "get": { + "tags": [ + "knowledge" + ], + "summary": "Get Knowledge", + "operationId": "get_knowledge_api_v1_knowledge__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/KnowledgeUserResponse" + }, + "type": "array", + "title": "Response Get Knowledge Api V1 Knowledge Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/knowledge/list": { + "get": { + "tags": [ + "knowledge" + ], + "summary": "Get Knowledge List", + "operationId": "get_knowledge_list_api_v1_knowledge_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/KnowledgeUserResponse" + }, + "type": "array", + "title": "Response Get Knowledge List Api V1 Knowledge List Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/knowledge/create": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Create New Knowledge", + "operationId": "create_new_knowledge_api_v1_knowledge_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KnowledgeForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeResponse" + }, + { + "type": "null" + } + ], + "title": "Response Create New Knowledge Api V1 Knowledge Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/knowledge/reindex": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Reindex Knowledge Files", + "operationId": "reindex_knowledge_files_api_v1_knowledge_reindex_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Reindex Knowledge Files Api V1 Knowledge Reindex Post" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/knowledge/{id}": { + "get": { + "tags": [ + "knowledge" + ], + "summary": "Get Knowledge By Id", + "operationId": "get_knowledge_by_id_api_v1_knowledge__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Knowledge By Id Api V1 Knowledge Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/update": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Update Knowledge By Id", + "operationId": "update_knowledge_by_id_api_v1_knowledge__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KnowledgeForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update Knowledge By Id Api V1 Knowledge Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/file/add": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Add File To Knowledge By Id", + "operationId": "add_file_to_knowledge_by_id_api_v1_knowledge__id__file_add_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KnowledgeFileIdForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Add File To Knowledge By Id Api V1 Knowledge Id File Add Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/file/update": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Update File From Knowledge By Id", + "operationId": "update_file_from_knowledge_by_id_api_v1_knowledge__id__file_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KnowledgeFileIdForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update File From Knowledge By Id Api V1 Knowledge Id File Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/file/remove": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Remove File From Knowledge By Id", + "operationId": "remove_file_from_knowledge_by_id_api_v1_knowledge__id__file_remove_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "delete_file", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Delete File" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/KnowledgeFileIdForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Remove File From Knowledge By Id Api V1 Knowledge Id File Remove Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/delete": { + "delete": { + "tags": [ + "knowledge" + ], + "summary": "Delete Knowledge By Id", + "operationId": "delete_knowledge_by_id_api_v1_knowledge__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Knowledge By Id Api V1 Knowledge Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/reset": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Reset Knowledge By Id", + "operationId": "reset_knowledge_by_id_api_v1_knowledge__id__reset_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeResponse" + }, + { + "type": "null" + } + ], + "title": "Response Reset Knowledge By Id Api V1 Knowledge Id Reset Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/knowledge/{id}/files/batch/add": { + "post": { + "tags": [ + "knowledge" + ], + "summary": "Add Files To Knowledge Batch", + "description": "Add multiple files to a knowledge base", + "operationId": "add_files_to_knowledge_batch_api_v1_knowledge__id__files_batch_add_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/KnowledgeFileIdForm" + }, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/KnowledgeFilesResponse" + }, + { + "type": "null" + } + ], + "title": "Response Add Files To Knowledge Batch Api V1 Knowledge Id Files Batch Add Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/prompts/": { + "get": { + "tags": [ + "prompts" + ], + "summary": "Get Prompts", + "operationId": "get_prompts_api_v1_prompts__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PromptModel" + }, + "type": "array", + "title": "Response Get Prompts Api V1 Prompts Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/prompts/list": { + "get": { + "tags": [ + "prompts" + ], + "summary": "Get Prompt List", + "operationId": "get_prompt_list_api_v1_prompts_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/PromptUserResponse" + }, + "type": "array", + "title": "Response Get Prompt List Api V1 Prompts List Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/prompts/create": { + "post": { + "tags": [ + "prompts" + ], + "summary": "Create New Prompt", + "operationId": "create_new_prompt_api_v1_prompts_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromptForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PromptModel" + }, + { + "type": "null" + } + ], + "title": "Response Create New Prompt Api V1 Prompts Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/prompts/command/{command}": { + "get": { + "tags": [ + "prompts" + ], + "summary": "Get Prompt By Command", + "operationId": "get_prompt_by_command_api_v1_prompts_command__command__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "command", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Command" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PromptModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Prompt By Command Api V1 Prompts Command Command Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/prompts/command/{command}/update": { + "post": { + "tags": [ + "prompts" + ], + "summary": "Update Prompt By Command", + "operationId": "update_prompt_by_command_api_v1_prompts_command__command__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "command", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Command" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PromptForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/PromptModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Prompt By Command Api V1 Prompts Command Command Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/prompts/command/{command}/delete": { + "delete": { + "tags": [ + "prompts" + ], + "summary": "Delete Prompt By Command", + "operationId": "delete_prompt_by_command_api_v1_prompts_command__command__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "command", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Command" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Prompt By Command Api V1 Prompts Command Command Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools", + "operationId": "get_tools_api_v1_tools__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ToolUserResponse" + }, + "type": "array", + "title": "Response Get Tools Api V1 Tools Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tools/list": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tool List", + "operationId": "get_tool_list_api_v1_tools_list_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ToolUserResponse" + }, + "type": "array", + "title": "Response Get Tool List Api V1 Tools List Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tools/load/url": { + "post": { + "tags": [ + "tools" + ], + "summary": "Load Tool From Url", + "operationId": "load_tool_from_url_api_v1_tools_load_url_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoadUrlForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Response Load Tool From Url Api V1 Tools Load Url Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tools/export": { + "get": { + "tags": [ + "tools" + ], + "summary": "Export Tools", + "operationId": "export_tools_api_v1_tools_export_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/ToolModel" + }, + "type": "array", + "title": "Response Export Tools Api V1 Tools Export Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tools/create": { + "post": { + "tags": [ + "tools" + ], + "summary": "Create New Tools", + "operationId": "create_new_tools_api_v1_tools_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolResponse" + }, + { + "type": "null" + } + ], + "title": "Response Create New Tools Api V1 Tools Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/tools/id/{id}": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools By Id", + "operationId": "get_tools_by_id_api_v1_tools_id__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Tools By Id Api V1 Tools Id Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/update": { + "post": { + "tags": [ + "tools" + ], + "summary": "Update Tools By Id", + "operationId": "update_tools_by_id_api_v1_tools_id__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ToolForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/ToolModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Tools By Id Api V1 Tools Id Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/delete": { + "delete": { + "tags": [ + "tools" + ], + "summary": "Delete Tools By Id", + "operationId": "delete_tools_by_id_api_v1_tools_id__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Tools By Id Api V1 Tools Id Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools Valves By Id", + "operationId": "get_tools_valves_by_id_api_v1_tools_id__id__valves_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Tools Valves By Id Api V1 Tools Id Id Valves Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves/spec": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools Valves Spec By Id", + "operationId": "get_tools_valves_spec_by_id_api_v1_tools_id__id__valves_spec_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Tools Valves Spec By Id Api V1 Tools Id Id Valves Spec Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves/update": { + "post": { + "tags": [ + "tools" + ], + "summary": "Update Tools Valves By Id", + "operationId": "update_tools_valves_by_id_api_v1_tools_id__id__valves_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Update Tools Valves By Id Api V1 Tools Id Id Valves Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves/user": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools User Valves By Id", + "operationId": "get_tools_user_valves_by_id_api_v1_tools_id__id__valves_user_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Tools User Valves By Id Api V1 Tools Id Id Valves User Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves/user/spec": { + "get": { + "tags": [ + "tools" + ], + "summary": "Get Tools User Valves Spec By Id", + "operationId": "get_tools_user_valves_spec_by_id_api_v1_tools_id__id__valves_user_spec_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Tools User Valves Spec By Id Api V1 Tools Id Id Valves User Spec Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/tools/id/{id}/valves/user/update": { + "post": { + "tags": [ + "tools" + ], + "summary": "Update Tools User Valves By Id", + "operationId": "update_tools_user_valves_by_id_api_v1_tools_id__id__valves_user_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Update Tools User Valves By Id Api V1 Tools Id Id Valves User Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/memories/ef": { + "get": { + "tags": [ + "memories" + ], + "summary": "Get Embeddings", + "operationId": "get_embeddings_api_v1_memories_ef_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/v1/memories/": { + "get": { + "tags": [ + "memories" + ], + "summary": "Get Memories", + "operationId": "get_memories_api_v1_memories__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/MemoryModel" + }, + "type": "array", + "title": "Response Get Memories Api V1 Memories Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/memories/add": { + "post": { + "tags": [ + "memories" + ], + "summary": "Add Memory", + "operationId": "add_memory_api_v1_memories_add_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddMemoryForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MemoryModel" + }, + { + "type": "null" + } + ], + "title": "Response Add Memory Api V1 Memories Add Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/memories/query": { + "post": { + "tags": [ + "memories" + ], + "summary": "Query Memory", + "operationId": "query_memory_api_v1_memories_query_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryMemoryForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/memories/reset": { + "post": { + "tags": [ + "memories" + ], + "summary": "Reset Memory From Vector Db", + "operationId": "reset_memory_from_vector_db_api_v1_memories_reset_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Reset Memory From Vector Db Api V1 Memories Reset Post" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/memories/delete/user": { + "delete": { + "tags": [ + "memories" + ], + "summary": "Delete Memory By User Id", + "operationId": "delete_memory_by_user_id_api_v1_memories_delete_user_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Memory By User Id Api V1 Memories Delete User Delete" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/memories/{memory_id}/update": { + "post": { + "tags": [ + "memories" + ], + "summary": "Update Memory By Id", + "operationId": "update_memory_by_id_api_v1_memories__memory_id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "memory_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Memory Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MemoryUpdateModel" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/MemoryModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Memory By Id Api V1 Memories Memory Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/memories/{memory_id}": { + "delete": { + "tags": [ + "memories" + ], + "summary": "Delete Memory By Id", + "operationId": "delete_memory_by_id_api_v1_memories__memory_id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "memory_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Memory Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Memory By Id Api V1 Memories Memory Id Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/folders/": { + "get": { + "tags": [ + "folders" + ], + "summary": "Get Folders", + "operationId": "get_folders_api_v1_folders__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FolderModel" + }, + "type": "array", + "title": "Response Get Folders Api V1 Folders Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "folders" + ], + "summary": "Create Folder", + "operationId": "create_folder_api_v1_folders__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/folders/{id}": { + "get": { + "tags": [ + "folders" + ], + "summary": "Get Folder By Id", + "operationId": "get_folder_by_id_api_v1_folders__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FolderModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Folder By Id Api V1 Folders Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "folders" + ], + "summary": "Delete Folder By Id", + "operationId": "delete_folder_by_id_api_v1_folders__id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/folders/{id}/update": { + "post": { + "tags": [ + "folders" + ], + "summary": "Update Folder Name By Id", + "operationId": "update_folder_name_by_id_api_v1_folders__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/folders/{id}/update/parent": { + "post": { + "tags": [ + "folders" + ], + "summary": "Update Folder Parent Id By Id", + "operationId": "update_folder_parent_id_by_id_api_v1_folders__id__update_parent_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderParentIdForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/folders/{id}/update/expanded": { + "post": { + "tags": [ + "folders" + ], + "summary": "Update Folder Is Expanded By Id", + "operationId": "update_folder_is_expanded_by_id_api_v1_folders__id__update_expanded_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderIsExpandedForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/groups/": { + "get": { + "tags": [ + "groups" + ], + "summary": "Get Groups", + "operationId": "get_groups_api_v1_groups__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/GroupResponse" + }, + "type": "array", + "title": "Response Get Groups Api V1 Groups Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/groups/create": { + "post": { + "tags": [ + "groups" + ], + "summary": "Create New Group", + "operationId": "create_new_group_api_v1_groups_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GroupForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/GroupResponse" + }, + { + "type": "null" + } + ], + "title": "Response Create New Group Api V1 Groups Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/groups/id/{id}": { + "get": { + "tags": [ + "groups" + ], + "summary": "Get Group By Id", + "operationId": "get_group_by_id_api_v1_groups_id__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/GroupResponse" + }, + { + "type": "null" + } + ], + "title": "Response Get Group By Id Api V1 Groups Id Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/groups/id/{id}/update": { + "post": { + "tags": [ + "groups" + ], + "summary": "Update Group By Id", + "operationId": "update_group_by_id_api_v1_groups_id__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GroupUpdateForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/GroupResponse" + }, + { + "type": "null" + } + ], + "title": "Response Update Group By Id Api V1 Groups Id Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/groups/id/{id}/users/add": { + "post": { + "tags": [ + "groups" + ], + "summary": "Add User To Group", + "operationId": "add_user_to_group_api_v1_groups_id__id__users_add_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserIdsForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/GroupResponse" + }, + { + "type": "null" + } + ], + "title": "Response Add User To Group Api V1 Groups Id Id Users Add Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/groups/id/{id}/users/remove": { + "post": { + "tags": [ + "groups" + ], + "summary": "Remove Users From Group", + "operationId": "remove_users_from_group_api_v1_groups_id__id__users_remove_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserIdsForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/GroupResponse" + }, + { + "type": "null" + } + ], + "title": "Response Remove Users From Group Api V1 Groups Id Id Users Remove Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/groups/id/{id}/delete": { + "delete": { + "tags": [ + "groups" + ], + "summary": "Delete Group By Id", + "operationId": "delete_group_by_id_api_v1_groups_id__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Group By Id Api V1 Groups Id Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/": { + "post": { + "tags": [ + "files" + ], + "summary": "Upload File", + "operationId": "upload_file_api_v1_files__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "process", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Process" + } + }, + { + "name": "process_in_background", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Process In Background" + } + } + ], + "requestBody": { + "required": true, + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_file_api_v1_files__post" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileModelResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "get": { + "tags": [ + "files" + ], + "summary": "List Files", + "operationId": "list_files_api_v1_files__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "content", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Content" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileModelResponse" + }, + "title": "Response List Files Api V1 Files Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/search": { + "get": { + "tags": [ + "files" + ], + "summary": "Search Files", + "description": "Search for files by filename with support for wildcard patterns.", + "operationId": "search_files_api_v1_files_search_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "filename", + "in": "query", + "required": true, + "schema": { + "type": "string", + "description": "Filename pattern to search for. Supports wildcards such as '*.txt'", + "title": "Filename" + }, + "description": "Filename pattern to search for. Supports wildcards such as '*.txt'" + }, + { + "name": "content", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": true, + "title": "Content" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/FileModelResponse" + }, + "title": "Response Search Files Api V1 Files Search Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/all": { + "delete": { + "tags": [ + "files" + ], + "summary": "Delete All Files", + "operationId": "delete_all_files_api_v1_files_all_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/files/{id}": { + "get": { + "tags": [ + "files" + ], + "summary": "Get File By Id", + "operationId": "get_file_by_id_api_v1_files__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FileModel" + }, + { + "type": "null" + } + ], + "title": "Response Get File By Id Api V1 Files Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "files" + ], + "summary": "Delete File By Id", + "operationId": "delete_file_by_id_api_v1_files__id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/process/status": { + "get": { + "tags": [ + "files" + ], + "summary": "Get File Process Status", + "operationId": "get_file_process_status_api_v1_files__id__process_status_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "stream", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Stream" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/data/content": { + "get": { + "tags": [ + "files" + ], + "summary": "Get File Data Content By Id", + "operationId": "get_file_data_content_by_id_api_v1_files__id__data_content_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/data/content/update": { + "post": { + "tags": [ + "files" + ], + "summary": "Update File Data Content By Id", + "operationId": "update_file_data_content_by_id_api_v1_files__id__data_content_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContentForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/content": { + "get": { + "tags": [ + "files" + ], + "summary": "Get File Content By Id", + "operationId": "get_file_content_by_id_api_v1_files__id__content_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + }, + { + "name": "attachment", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Attachment" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/content/html": { + "get": { + "tags": [ + "files" + ], + "summary": "Get Html File Content By Id", + "operationId": "get_html_file_content_by_id_api_v1_files__id__content_html_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/files/{id}/content/{file_name}": { + "get": { + "tags": [ + "files" + ], + "summary": "Get File Content By Id", + "operationId": "get_file_content_by_id_api_v1_files__id__content__file_name__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Functions", + "operationId": "get_functions_api_v1_functions__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FunctionResponse" + }, + "type": "array", + "title": "Response Get Functions Api V1 Functions Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/functions/export": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Functions", + "operationId": "get_functions_api_v1_functions_export_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "include_valves", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Include Valves" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionModel" + }, + { + "$ref": "#/components/schemas/FunctionWithValvesModel" + } + ] + }, + "title": "Response Get Functions Api V1 Functions Export Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/load/url": { + "post": { + "tags": [ + "functions" + ], + "summary": "Load Function From Url", + "operationId": "load_function_from_url_api_v1_functions_load_url_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LoadUrlForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Response Load Function From Url Api V1 Functions Load Url Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/functions/sync": { + "post": { + "tags": [ + "functions" + ], + "summary": "Sync Functions", + "operationId": "sync_functions_api_v1_functions_sync_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SyncFunctionsForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FunctionWithValvesModel" + }, + "type": "array", + "title": "Response Sync Functions Api V1 Functions Sync Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/functions/create": { + "post": { + "tags": [ + "functions" + ], + "summary": "Create New Function", + "operationId": "create_new_function_api_v1_functions_create_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FunctionForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionResponse" + }, + { + "type": "null" + } + ], + "title": "Response Create New Function Api V1 Functions Create Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/functions/id/{id}": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Function By Id", + "operationId": "get_function_by_id_api_v1_functions_id__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionModel" + }, + { + "type": "null" + } + ], + "title": "Response Get Function By Id Api V1 Functions Id Id Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/toggle": { + "post": { + "tags": [ + "functions" + ], + "summary": "Toggle Function By Id", + "operationId": "toggle_function_by_id_api_v1_functions_id__id__toggle_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionModel" + }, + { + "type": "null" + } + ], + "title": "Response Toggle Function By Id Api V1 Functions Id Id Toggle Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/toggle/global": { + "post": { + "tags": [ + "functions" + ], + "summary": "Toggle Global By Id", + "operationId": "toggle_global_by_id_api_v1_functions_id__id__toggle_global_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionModel" + }, + { + "type": "null" + } + ], + "title": "Response Toggle Global By Id Api V1 Functions Id Id Toggle Global Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/update": { + "post": { + "tags": [ + "functions" + ], + "summary": "Update Function By Id", + "operationId": "update_function_by_id_api_v1_functions_id__id__update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FunctionForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/FunctionModel" + }, + { + "type": "null" + } + ], + "title": "Response Update Function By Id Api V1 Functions Id Id Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/delete": { + "delete": { + "tags": [ + "functions" + ], + "summary": "Delete Function By Id", + "operationId": "delete_function_by_id_api_v1_functions_id__id__delete_delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Function By Id Api V1 Functions Id Id Delete Delete" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Function Valves By Id", + "operationId": "get_function_valves_by_id_api_v1_functions_id__id__valves_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Function Valves By Id Api V1 Functions Id Id Valves Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves/spec": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Function Valves Spec By Id", + "operationId": "get_function_valves_spec_by_id_api_v1_functions_id__id__valves_spec_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Function Valves Spec By Id Api V1 Functions Id Id Valves Spec Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves/update": { + "post": { + "tags": [ + "functions" + ], + "summary": "Update Function Valves By Id", + "operationId": "update_function_valves_by_id_api_v1_functions_id__id__valves_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Update Function Valves By Id Api V1 Functions Id Id Valves Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves/user": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Function User Valves By Id", + "operationId": "get_function_user_valves_by_id_api_v1_functions_id__id__valves_user_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Function User Valves By Id Api V1 Functions Id Id Valves User Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves/user/spec": { + "get": { + "tags": [ + "functions" + ], + "summary": "Get Function User Valves Spec By Id", + "operationId": "get_function_user_valves_spec_by_id_api_v1_functions_id__id__valves_user_spec_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Get Function User Valves Spec By Id Api V1 Functions Id Id Valves User Spec Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/functions/id/{id}/valves/user/update": { + "post": { + "tags": [ + "functions" + ], + "summary": "Update Function User Valves By Id", + "operationId": "update_function_user_valves_by_id_api_v1_functions_id__id__valves_user_update_post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "type": "object", + "additionalProperties": true + }, + { + "type": "null" + } + ], + "title": "Response Update Function User Valves By Id Api V1 Functions Id Id Valves User Update Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/evaluations/config": { + "get": { + "tags": [ + "evaluations" + ], + "summary": "Get Config", + "operationId": "get_config_api_v1_evaluations_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "tags": [ + "evaluations" + ], + "summary": "Update Config", + "operationId": "update_config_api_v1_evaluations_config_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateConfigForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedbacks/all": { + "get": { + "tags": [ + "evaluations" + ], + "summary": "Get All Feedbacks", + "operationId": "get_all_feedbacks_api_v1_evaluations_feedbacks_all_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FeedbackUserResponse" + }, + "type": "array", + "title": "Response Get All Feedbacks Api V1 Evaluations Feedbacks All Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "delete": { + "tags": [ + "evaluations" + ], + "summary": "Delete All Feedbacks", + "operationId": "delete_all_feedbacks_api_v1_evaluations_feedbacks_all_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedbacks/all/export": { + "get": { + "tags": [ + "evaluations" + ], + "summary": "Get All Feedbacks", + "operationId": "get_all_feedbacks_api_v1_evaluations_feedbacks_all_export_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FeedbackModel" + }, + "type": "array", + "title": "Response Get All Feedbacks Api V1 Evaluations Feedbacks All Export Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedbacks/user": { + "get": { + "tags": [ + "evaluations" + ], + "summary": "Get Feedbacks", + "operationId": "get_feedbacks_api_v1_evaluations_feedbacks_user_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/FeedbackUserResponse" + }, + "type": "array", + "title": "Response Get Feedbacks Api V1 Evaluations Feedbacks User Get" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedbacks": { + "delete": { + "tags": [ + "evaluations" + ], + "summary": "Delete Feedbacks", + "operationId": "delete_feedbacks_api_v1_evaluations_feedbacks_delete", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "boolean", + "title": "Response Delete Feedbacks Api V1 Evaluations Feedbacks Delete" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedback": { + "post": { + "tags": [ + "evaluations" + ], + "summary": "Create Feedback", + "operationId": "create_feedback_api_v1_evaluations_feedback_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackModel" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/evaluations/feedback/{id}": { + "get": { + "tags": [ + "evaluations" + ], + "summary": "Get Feedback By Id", + "operationId": "get_feedback_by_id_api_v1_evaluations_feedback__id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackModel" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "post": { + "tags": [ + "evaluations" + ], + "summary": "Update Feedback By Id", + "operationId": "update_feedback_by_id_api_v1_evaluations_feedback__id__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackForm" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FeedbackModel" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + }, + "delete": { + "tags": [ + "evaluations" + ], + "summary": "Delete Feedback By Id", + "operationId": "delete_feedback_by_id_api_v1_evaluations_feedback__id__delete", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/utils/gravatar": { + "get": { + "tags": [ + "utils" + ], + "summary": "Get Gravatar", + "operationId": "get_gravatar_api_v1_utils_gravatar_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "email", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Email" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/v1/utils/code/format": { + "post": { + "tags": [ + "utils" + ], + "summary": "Format Code", + "operationId": "format_code_api_v1_utils_code_format_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CodeForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/utils/code/execute": { + "post": { + "tags": [ + "utils" + ], + "summary": "Execute Code", + "operationId": "execute_code_api_v1_utils_code_execute_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CodeForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/utils/markdown": { + "post": { + "tags": [ + "utils" + ], + "summary": "Get Html From Markdown", + "operationId": "get_html_from_markdown_api_v1_utils_markdown_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MarkdownForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/utils/pdf": { + "post": { + "tags": [ + "utils" + ], + "summary": "Download Chat As Pdf", + "operationId": "download_chat_as_pdf_api_v1_utils_pdf_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChatTitleMessagesForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/utils/db/download": { + "get": { + "tags": [ + "utils" + ], + "summary": "Download Db", + "operationId": "download_db_api_v1_utils_db_download_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/utils/litellm/config": { + "get": { + "tags": [ + "utils" + ], + "summary": "Download Litellm Config Yaml", + "operationId": "download_litellm_config_yaml_api_v1_utils_litellm_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/models": { + "get": { + "summary": "Get Models", + "operationId": "get_models_api_v1_models_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "refresh", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Refresh" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/models": { + "get": { + "summary": "Get Models", + "operationId": "get_models_api_models_get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "refresh", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "default": false, + "title": "Refresh" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/models/base": { + "get": { + "summary": "Get Base Models", + "operationId": "get_base_models_api_models_base_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/embeddings": { + "post": { + "summary": "Embeddings", + "description": "OpenAI-compatible embeddings endpoint.\n\nThis handler:\n - Performs user/model checks and dispatches to the correct backend.\n - Supports OpenAI, Ollama, arena models, pipelines, and any compatible provider.\n\nArgs:\n request (Request): Request context.\n form_data (dict): OpenAI-like payload (e.g., {\"model\": \"...\", \"input\": [...]})\n user (UserModel): Authenticated user.\n\nReturns:\n dict: OpenAI-compatible embeddings response.", + "operationId": "embeddings_api_v1_embeddings_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/embeddings": { + "post": { + "summary": "Embeddings", + "description": "OpenAI-compatible embeddings endpoint.\n\nThis handler:\n - Performs user/model checks and dispatches to the correct backend.\n - Supports OpenAI, Ollama, arena models, pipelines, and any compatible provider.\n\nArgs:\n request (Request): Request context.\n form_data (dict): OpenAI-like payload (e.g., {\"model\": \"...\", \"input\": [...]})\n user (UserModel): Authenticated user.\n\nReturns:\n dict: OpenAI-compatible embeddings response.", + "operationId": "embeddings_api_embeddings_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/v1/chat/completions": { + "post": { + "summary": "Chat Completion", + "operationId": "chat_completion_api_v1_chat_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/chat/completions": { + "post": { + "summary": "Chat Completion", + "operationId": "chat_completion_api_chat_completions_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/chat/completed": { + "post": { + "summary": "Chat Completed", + "operationId": "chat_completed_api_chat_completed_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Form Data" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/chat/actions/{action_id}": { + "post": { + "summary": "Chat Action", + "operationId": "chat_action_api_chat_actions__action_id__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "action_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Action Id" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true, + "title": "Form Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/tasks/stop/{task_id}": { + "post": { + "summary": "Stop Task Endpoint", + "operationId": "stop_task_endpoint_api_tasks_stop__task_id__post", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Task Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/tasks": { + "get": { + "summary": "List Tasks Endpoint", + "operationId": "list_tasks_endpoint_api_tasks_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/tasks/chat/{chat_id}": { + "get": { + "summary": "List Tasks By Chat Id Endpoint", + "operationId": "list_tasks_by_chat_id_endpoint_api_tasks_chat__chat_id__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "chat_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Chat Id" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/api/config": { + "get": { + "summary": "Get App Config", + "operationId": "get_app_config_api_config_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/webhook": { + "get": { + "summary": "Get Webhook Url", + "operationId": "get_webhook_url_api_webhook_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + }, + "post": { + "summary": "Update Webhook Url", + "operationId": "update_webhook_url_api_webhook_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UrlForm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/version": { + "get": { + "summary": "Get App Version", + "operationId": "get_app_version_api_version_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/version/updates": { + "get": { + "summary": "Get App Latest Release Version", + "operationId": "get_app_latest_release_version_api_version_updates_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/api/changelog": { + "get": { + "summary": "Get App Changelog", + "operationId": "get_app_changelog_api_changelog_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/api/usage": { + "get": { + "summary": "Get Current Usage", + "description": "Get current usage statistics for Open WebUI.\nThis is an experimental endpoint and subject to change.", + "operationId": "get_current_usage_api_usage_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + }, + "security": [ + { + "HTTPBearer": [] + } + ] + } + }, + "/oauth/{provider}/login": { + "get": { + "summary": "Oauth Login", + "operationId": "oauth_login_oauth__provider__login_get", + "parameters": [ + { + "name": "provider", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Provider" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/oauth/{provider}/callback": { + "get": { + "summary": "Oauth Callback", + "operationId": "oauth_callback_oauth__provider__callback_get", + "parameters": [ + { + "name": "provider", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Provider" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/manifest.json": { + "get": { + "summary": "Get Manifest Json", + "operationId": "get_manifest_json_manifest_json_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/opensearch.xml": { + "get": { + "summary": "Get Opensearch Xml", + "operationId": "get_opensearch_xml_opensearch_xml_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health": { + "get": { + "summary": "Healthcheck", + "operationId": "healthcheck_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/health/db": { + "get": { + "summary": "Healthcheck With Db", + "operationId": "healthcheck_with_db_health_db_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/cache/{path}": { + "get": { + "summary": "Serve Cache File", + "operationId": "serve_cache_file_cache__path__get", + "security": [ + { + "HTTPBearer": [] + } + ], + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "type": "string", + "title": "Path" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AddMemoryForm": { + "properties": { + "content": { + "type": "string", + "title": "Content" + } + }, + "type": "object", + "required": [ + "content" + ], + "title": "AddMemoryForm" + }, + "AddPipelineForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "urlIdx": { + "type": "integer", + "title": "Urlidx" + } + }, + "type": "object", + "required": [ + "url", + "urlIdx" + ], + "title": "AddPipelineForm" + }, + "AddUserForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "password": { + "type": "string", + "title": "Password" + }, + "profile_image_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Profile Image Url", + "default": "/user.png" + }, + "role": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Role", + "default": "pending" + } + }, + "type": "object", + "required": [ + "name", + "email", + "password" + ], + "title": "AddUserForm" + }, + "AdminConfig": { + "properties": { + "SHOW_ADMIN_DETAILS": { + "type": "boolean", + "title": "Show Admin Details" + }, + "WEBUI_URL": { + "type": "string", + "title": "Webui Url" + }, + "ENABLE_SIGNUP": { + "type": "boolean", + "title": "Enable Signup" + }, + "ENABLE_API_KEY": { + "type": "boolean", + "title": "Enable Api Key" + }, + "ENABLE_API_KEY_ENDPOINT_RESTRICTIONS": { + "type": "boolean", + "title": "Enable Api Key Endpoint Restrictions" + }, + "API_KEY_ALLOWED_ENDPOINTS": { + "type": "string", + "title": "Api Key Allowed Endpoints" + }, + "DEFAULT_USER_ROLE": { + "type": "string", + "title": "Default User Role" + }, + "JWT_EXPIRES_IN": { + "type": "string", + "title": "Jwt Expires In" + }, + "ENABLE_COMMUNITY_SHARING": { + "type": "boolean", + "title": "Enable Community Sharing" + }, + "ENABLE_MESSAGE_RATING": { + "type": "boolean", + "title": "Enable Message Rating" + }, + "ENABLE_CHANNELS": { + "type": "boolean", + "title": "Enable Channels" + }, + "ENABLE_NOTES": { + "type": "boolean", + "title": "Enable Notes" + }, + "ENABLE_USER_WEBHOOKS": { + "type": "boolean", + "title": "Enable User Webhooks" + }, + "PENDING_USER_OVERLAY_TITLE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Pending User Overlay Title" + }, + "PENDING_USER_OVERLAY_CONTENT": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Pending User Overlay Content" + }, + "RESPONSE_WATERMARK": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Response Watermark" + } + }, + "type": "object", + "required": [ + "SHOW_ADMIN_DETAILS", + "WEBUI_URL", + "ENABLE_SIGNUP", + "ENABLE_API_KEY", + "ENABLE_API_KEY_ENDPOINT_RESTRICTIONS", + "API_KEY_ALLOWED_ENDPOINTS", + "DEFAULT_USER_ROLE", + "JWT_EXPIRES_IN", + "ENABLE_COMMUNITY_SHARING", + "ENABLE_MESSAGE_RATING", + "ENABLE_CHANNELS", + "ENABLE_NOTES", + "ENABLE_USER_WEBHOOKS" + ], + "title": "AdminConfig" + }, + "ApiKey": { + "properties": { + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Key" + } + }, + "type": "object", + "title": "ApiKey" + }, + "AudioConfigUpdateForm": { + "properties": { + "tts": { + "$ref": "#/components/schemas/TTSConfigForm" + }, + "stt": { + "$ref": "#/components/schemas/STTConfigForm" + } + }, + "type": "object", + "required": [ + "tts", + "stt" + ], + "title": "AudioConfigUpdateForm" + }, + "Automatic1111ConfigForm": { + "properties": { + "AUTOMATIC1111_BASE_URL": { + "type": "string", + "title": "Automatic1111 Base Url" + }, + "AUTOMATIC1111_API_AUTH": { + "type": "string", + "title": "Automatic1111 Api Auth" + }, + "AUTOMATIC1111_CFG_SCALE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Automatic1111 Cfg Scale" + }, + "AUTOMATIC1111_SAMPLER": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Automatic1111 Sampler" + }, + "AUTOMATIC1111_SCHEDULER": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Automatic1111 Scheduler" + } + }, + "type": "object", + "required": [ + "AUTOMATIC1111_BASE_URL", + "AUTOMATIC1111_API_AUTH", + "AUTOMATIC1111_CFG_SCALE", + "AUTOMATIC1111_SAMPLER", + "AUTOMATIC1111_SCHEDULER" + ], + "title": "Automatic1111ConfigForm" + }, + "AzureOpenAIConfigForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "key": { + "type": "string", + "title": "Key" + }, + "version": { + "type": "string", + "title": "Version" + } + }, + "type": "object", + "required": [ + "url", + "key", + "version" + ], + "title": "AzureOpenAIConfigForm" + }, + "BannerModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "type": { + "type": "string", + "title": "Type" + }, + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + }, + "content": { + "type": "string", + "title": "Content" + }, + "dismissible": { + "type": "boolean", + "title": "Dismissible" + }, + "timestamp": { + "type": "integer", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "id", + "type", + "content", + "dismissible", + "timestamp" + ], + "title": "BannerModel" + }, + "BatchProcessFilesForm": { + "properties": { + "files": { + "items": { + "$ref": "#/components/schemas/FileModel" + }, + "type": "array", + "title": "Files" + }, + "collection_name": { + "type": "string", + "title": "Collection Name" + } + }, + "type": "object", + "required": [ + "files", + "collection_name" + ], + "title": "BatchProcessFilesForm" + }, + "BatchProcessFilesResponse": { + "properties": { + "results": { + "items": { + "$ref": "#/components/schemas/BatchProcessFilesResult" + }, + "type": "array", + "title": "Results" + }, + "errors": { + "items": { + "$ref": "#/components/schemas/BatchProcessFilesResult" + }, + "type": "array", + "title": "Errors" + } + }, + "type": "object", + "required": [ + "results", + "errors" + ], + "title": "BatchProcessFilesResponse" + }, + "BatchProcessFilesResult": { + "properties": { + "file_id": { + "type": "string", + "title": "File Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "file_id", + "status" + ], + "title": "BatchProcessFilesResult" + }, + "Body_transcription_api_v1_audio_transcriptions_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "language": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Language" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_transcription_api_v1_audio_transcriptions_post" + }, + "Body_upload_file_api_v1_files__post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + }, + "metadata": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Metadata" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_file_api_v1_files__post" + }, + "Body_upload_model_ollama_models_upload__url_idx__post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_model_ollama_models_upload__url_idx__post" + }, + "Body_upload_model_ollama_models_upload_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "file" + ], + "title": "Body_upload_model_ollama_models_upload_post" + }, + "Body_upload_pipeline_api_v1_pipelines_upload_post": { + "properties": { + "urlIdx": { + "type": "integer", + "title": "Urlidx" + }, + "file": { + "type": "string", + "format": "binary", + "title": "File" + } + }, + "type": "object", + "required": [ + "urlIdx", + "file" + ], + "title": "Body_upload_pipeline_api_v1_pipelines_upload_post" + }, + "ChannelForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "ChannelForm" + }, + "ChannelModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Type" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "created_at", + "updated_at" + ], + "title": "ChannelModel" + }, + "ChatFolderIdForm": { + "properties": { + "folder_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Folder Id" + } + }, + "type": "object", + "title": "ChatFolderIdForm" + }, + "ChatForm": { + "properties": { + "chat": { + "additionalProperties": true, + "type": "object", + "title": "Chat" + }, + "folder_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Folder Id" + } + }, + "type": "object", + "required": [ + "chat" + ], + "title": "ChatForm" + }, + "ChatImportForm": { + "properties": { + "chat": { + "additionalProperties": true, + "type": "object", + "title": "Chat" + }, + "folder_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Folder Id" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta", + "default": {} + }, + "pinned": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Pinned", + "default": false + }, + "created_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "updated_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "chat" + ], + "title": "ChatImportForm" + }, + "ChatPermissions": { + "properties": { + "controls": { + "type": "boolean", + "title": "Controls", + "default": true + }, + "valves": { + "type": "boolean", + "title": "Valves", + "default": true + }, + "system_prompt": { + "type": "boolean", + "title": "System Prompt", + "default": true + }, + "params": { + "type": "boolean", + "title": "Params", + "default": true + }, + "file_upload": { + "type": "boolean", + "title": "File Upload", + "default": true + }, + "delete": { + "type": "boolean", + "title": "Delete", + "default": true + }, + "delete_message": { + "type": "boolean", + "title": "Delete Message", + "default": true + }, + "continue_response": { + "type": "boolean", + "title": "Continue Response", + "default": true + }, + "regenerate_response": { + "type": "boolean", + "title": "Regenerate Response", + "default": true + }, + "rate_response": { + "type": "boolean", + "title": "Rate Response", + "default": true + }, + "edit": { + "type": "boolean", + "title": "Edit", + "default": true + }, + "share": { + "type": "boolean", + "title": "Share", + "default": true + }, + "export": { + "type": "boolean", + "title": "Export", + "default": true + }, + "stt": { + "type": "boolean", + "title": "Stt", + "default": true + }, + "tts": { + "type": "boolean", + "title": "Tts", + "default": true + }, + "call": { + "type": "boolean", + "title": "Call", + "default": true + }, + "multiple_models": { + "type": "boolean", + "title": "Multiple Models", + "default": true + }, + "temporary": { + "type": "boolean", + "title": "Temporary", + "default": true + }, + "temporary_enforced": { + "type": "boolean", + "title": "Temporary Enforced", + "default": false + } + }, + "type": "object", + "title": "ChatPermissions" + }, + "ChatResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "chat": { + "additionalProperties": true, + "type": "object", + "title": "Chat" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "share_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Share Id" + }, + "archived": { + "type": "boolean", + "title": "Archived" + }, + "pinned": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Pinned", + "default": false + }, + "meta": { + "additionalProperties": true, + "type": "object", + "title": "Meta", + "default": {} + }, + "folder_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Folder Id" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "title", + "chat", + "updated_at", + "created_at", + "archived" + ], + "title": "ChatResponse" + }, + "ChatTitleIdResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "title", + "updated_at", + "created_at" + ], + "title": "ChatTitleIdResponse" + }, + "ChatTitleMessagesForm": { + "properties": { + "title": { + "type": "string", + "title": "Title" + }, + "messages": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Messages" + } + }, + "type": "object", + "required": [ + "title", + "messages" + ], + "title": "ChatTitleMessagesForm" + }, + "CloneForm": { + "properties": { + "title": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Title" + } + }, + "type": "object", + "title": "CloneForm" + }, + "CodeForm": { + "properties": { + "code": { + "type": "string", + "title": "Code" + } + }, + "type": "object", + "required": [ + "code" + ], + "title": "CodeForm" + }, + "CodeInterpreterConfigForm": { + "properties": { + "ENABLE_CODE_EXECUTION": { + "type": "boolean", + "title": "Enable Code Execution" + }, + "CODE_EXECUTION_ENGINE": { + "type": "string", + "title": "Code Execution Engine" + }, + "CODE_EXECUTION_JUPYTER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Execution Jupyter Url" + }, + "CODE_EXECUTION_JUPYTER_AUTH": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Execution Jupyter Auth" + }, + "CODE_EXECUTION_JUPYTER_AUTH_TOKEN": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Execution Jupyter Auth Token" + }, + "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Execution Jupyter Auth Password" + }, + "CODE_EXECUTION_JUPYTER_TIMEOUT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Code Execution Jupyter Timeout" + }, + "ENABLE_CODE_INTERPRETER": { + "type": "boolean", + "title": "Enable Code Interpreter" + }, + "CODE_INTERPRETER_ENGINE": { + "type": "string", + "title": "Code Interpreter Engine" + }, + "CODE_INTERPRETER_PROMPT_TEMPLATE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Prompt Template" + }, + "CODE_INTERPRETER_JUPYTER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Jupyter Url" + }, + "CODE_INTERPRETER_JUPYTER_AUTH": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Jupyter Auth" + }, + "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Jupyter Auth Token" + }, + "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Jupyter Auth Password" + }, + "CODE_INTERPRETER_JUPYTER_TIMEOUT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Code Interpreter Jupyter Timeout" + } + }, + "type": "object", + "required": [ + "ENABLE_CODE_EXECUTION", + "CODE_EXECUTION_ENGINE", + "CODE_EXECUTION_JUPYTER_URL", + "CODE_EXECUTION_JUPYTER_AUTH", + "CODE_EXECUTION_JUPYTER_AUTH_TOKEN", + "CODE_EXECUTION_JUPYTER_AUTH_PASSWORD", + "CODE_EXECUTION_JUPYTER_TIMEOUT", + "ENABLE_CODE_INTERPRETER", + "CODE_INTERPRETER_ENGINE", + "CODE_INTERPRETER_PROMPT_TEMPLATE", + "CODE_INTERPRETER_JUPYTER_URL", + "CODE_INTERPRETER_JUPYTER_AUTH", + "CODE_INTERPRETER_JUPYTER_AUTH_TOKEN", + "CODE_INTERPRETER_JUPYTER_AUTH_PASSWORD", + "CODE_INTERPRETER_JUPYTER_TIMEOUT" + ], + "title": "CodeInterpreterConfigForm" + }, + "ComfyUIConfigForm": { + "properties": { + "COMFYUI_BASE_URL": { + "type": "string", + "title": "Comfyui Base Url" + }, + "COMFYUI_API_KEY": { + "type": "string", + "title": "Comfyui Api Key" + }, + "COMFYUI_WORKFLOW": { + "type": "string", + "title": "Comfyui Workflow" + }, + "COMFYUI_WORKFLOW_NODES": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Comfyui Workflow Nodes" + } + }, + "type": "object", + "required": [ + "COMFYUI_BASE_URL", + "COMFYUI_API_KEY", + "COMFYUI_WORKFLOW", + "COMFYUI_WORKFLOW_NODES" + ], + "title": "ComfyUIConfigForm" + }, + "ConnectionsConfigForm": { + "properties": { + "ENABLE_DIRECT_CONNECTIONS": { + "type": "boolean", + "title": "Enable Direct Connections" + }, + "ENABLE_BASE_MODELS_CACHE": { + "type": "boolean", + "title": "Enable Base Models Cache" + } + }, + "type": "object", + "required": [ + "ENABLE_DIRECT_CONNECTIONS", + "ENABLE_BASE_MODELS_CACHE" + ], + "title": "ConnectionsConfigForm" + }, + "ContentForm": { + "properties": { + "content": { + "type": "string", + "title": "Content" + } + }, + "type": "object", + "required": [ + "content" + ], + "title": "ContentForm" + }, + "CopyModelForm": { + "properties": { + "source": { + "type": "string", + "title": "Source" + }, + "destination": { + "type": "string", + "title": "Destination" + } + }, + "type": "object", + "required": [ + "source", + "destination" + ], + "title": "CopyModelForm" + }, + "CreateModelForm": { + "properties": { + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model" + }, + "stream": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stream" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Path" + } + }, + "additionalProperties": true, + "type": "object", + "title": "CreateModelForm" + }, + "DeleteForm": { + "properties": { + "collection_name": { + "type": "string", + "title": "Collection Name" + }, + "file_id": { + "type": "string", + "title": "File Id" + } + }, + "type": "object", + "required": [ + "collection_name", + "file_id" + ], + "title": "DeleteForm" + }, + "DeletePipelineForm": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "urlIdx": { + "type": "integer", + "title": "Urlidx" + } + }, + "type": "object", + "required": [ + "id", + "urlIdx" + ], + "title": "DeletePipelineForm" + }, + "EmbeddingModelUpdateForm": { + "properties": { + "openai_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__routers__retrieval__OpenAIConfigForm" + }, + { + "type": "null" + } + ] + }, + "ollama_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__routers__retrieval__OllamaConfigForm" + }, + { + "type": "null" + } + ] + }, + "azure_openai_config": { + "anyOf": [ + { + "$ref": "#/components/schemas/AzureOpenAIConfigForm" + }, + { + "type": "null" + } + ] + }, + "embedding_engine": { + "type": "string", + "title": "Embedding Engine" + }, + "embedding_model": { + "type": "string", + "title": "Embedding Model" + }, + "embedding_batch_size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Embedding Batch Size", + "default": 1 + } + }, + "type": "object", + "required": [ + "embedding_engine", + "embedding_model" + ], + "title": "EmbeddingModelUpdateForm" + }, + "EventForm": { + "properties": { + "type": { + "type": "string", + "title": "Type" + }, + "data": { + "additionalProperties": true, + "type": "object", + "title": "Data" + } + }, + "type": "object", + "required": [ + "type", + "data" + ], + "title": "EventForm" + }, + "FeaturesPermissions": { + "properties": { + "direct_tool_servers": { + "type": "boolean", + "title": "Direct Tool Servers", + "default": false + }, + "web_search": { + "type": "boolean", + "title": "Web Search", + "default": true + }, + "image_generation": { + "type": "boolean", + "title": "Image Generation", + "default": true + }, + "code_interpreter": { + "type": "boolean", + "title": "Code Interpreter", + "default": true + }, + "notes": { + "type": "boolean", + "title": "Notes", + "default": true + } + }, + "type": "object", + "title": "FeaturesPermissions" + }, + "FeedbackForm": { + "properties": { + "type": { + "type": "string", + "title": "Type" + }, + "data": { + "anyOf": [ + { + "$ref": "#/components/schemas/RatingData" + }, + { + "type": "null" + } + ] + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "snapshot": { + "anyOf": [ + { + "$ref": "#/components/schemas/SnapshotData" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "type" + ], + "title": "FeedbackForm" + }, + "FeedbackModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "version": { + "type": "integer", + "title": "Version" + }, + "type": { + "type": "string", + "title": "Type" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "snapshot": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Snapshot" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "version", + "type", + "created_at", + "updated_at" + ], + "title": "FeedbackModel" + }, + "FeedbackUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "version": { + "type": "integer", + "title": "Version" + }, + "type": { + "type": "string", + "title": "Type" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__routers__evaluations__UserResponse" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "version", + "type", + "created_at", + "updated_at" + ], + "title": "FeedbackUserResponse" + }, + "FileMeta": { + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "content_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Type" + }, + "size": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Size" + } + }, + "additionalProperties": true, + "type": "object", + "title": "FileMeta" + }, + "FileMetadataResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "meta": { + "additionalProperties": true, + "type": "object", + "title": "Meta" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "meta", + "created_at", + "updated_at" + ], + "title": "FileMetadataResponse" + }, + "FileModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Hash" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Path" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Created At" + }, + "updated_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "filename", + "created_at", + "updated_at" + ], + "title": "FileModel" + }, + "FileModelResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "hash": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Hash" + }, + "filename": { + "type": "string", + "title": "Filename" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "$ref": "#/components/schemas/FileMeta" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "id", + "user_id", + "filename", + "meta", + "created_at", + "updated_at" + ], + "title": "FileModelResponse" + }, + "FolderForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "name" + ], + "title": "FolderForm" + }, + "FolderIsExpandedForm": { + "properties": { + "is_expanded": { + "type": "boolean", + "title": "Is Expanded" + } + }, + "type": "object", + "required": [ + "is_expanded" + ], + "title": "FolderIsExpandedForm" + }, + "FolderModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "items": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Items" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "is_expanded": { + "type": "boolean", + "title": "Is Expanded", + "default": false + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "created_at", + "updated_at" + ], + "title": "FolderModel" + }, + "FolderParentIdForm": { + "properties": { + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + } + }, + "type": "object", + "title": "FolderParentIdForm" + }, + "FolderUpdateForm": { + "properties": { + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Name" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + } + }, + "additionalProperties": true, + "type": "object", + "title": "FolderUpdateForm" + }, + "FunctionForm": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "content": { + "type": "string", + "title": "Content" + }, + "meta": { + "$ref": "#/components/schemas/FunctionMeta" + } + }, + "type": "object", + "required": [ + "id", + "name", + "content", + "meta" + ], + "title": "FunctionForm" + }, + "FunctionMeta": { + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "manifest": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Manifest", + "default": {} + } + }, + "type": "object", + "title": "FunctionMeta" + }, + "FunctionModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "type": { + "type": "string", + "title": "Type" + }, + "content": { + "type": "string", + "title": "Content" + }, + "meta": { + "$ref": "#/components/schemas/FunctionMeta" + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "default": false + }, + "is_global": { + "type": "boolean", + "title": "Is Global", + "default": false + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "type", + "content", + "meta", + "updated_at", + "created_at" + ], + "title": "FunctionModel" + }, + "FunctionResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "type": { + "type": "string", + "title": "Type" + }, + "name": { + "type": "string", + "title": "Name" + }, + "meta": { + "$ref": "#/components/schemas/FunctionMeta" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "is_global": { + "type": "boolean", + "title": "Is Global" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "type", + "name", + "meta", + "is_active", + "is_global", + "updated_at", + "created_at" + ], + "title": "FunctionResponse" + }, + "FunctionWithValvesModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "type": { + "type": "string", + "title": "Type" + }, + "content": { + "type": "string", + "title": "Content" + }, + "meta": { + "$ref": "#/components/schemas/FunctionMeta" + }, + "valves": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Valves" + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "default": false + }, + "is_global": { + "type": "boolean", + "title": "Is Global", + "default": false + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "type", + "content", + "meta", + "updated_at", + "created_at" + ], + "title": "FunctionWithValvesModel" + }, + "GeminiConfigForm": { + "properties": { + "GEMINI_API_BASE_URL": { + "type": "string", + "title": "Gemini Api Base Url" + }, + "GEMINI_API_KEY": { + "type": "string", + "title": "Gemini Api Key" + } + }, + "type": "object", + "required": [ + "GEMINI_API_BASE_URL", + "GEMINI_API_KEY" + ], + "title": "GeminiConfigForm" + }, + "GenerateCompletionForm": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "prompt": { + "type": "string", + "title": "Prompt" + }, + "suffix": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Suffix" + }, + "images": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Images" + }, + "format": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Format" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "system": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "System" + }, + "template": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Template" + }, + "context": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Context" + }, + "stream": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stream", + "default": true + }, + "raw": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Raw" + }, + "keep_alive": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Keep Alive" + } + }, + "type": "object", + "required": [ + "model", + "prompt" + ], + "title": "GenerateCompletionForm" + }, + "GenerateEmbedForm": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "input": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "string" + } + ], + "title": "Input" + }, + "truncate": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Truncate" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "keep_alive": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Keep Alive" + } + }, + "type": "object", + "required": [ + "model", + "input" + ], + "title": "GenerateEmbedForm" + }, + "GenerateEmbeddingsForm": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "prompt": { + "type": "string", + "title": "Prompt" + }, + "options": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Options" + }, + "keep_alive": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Keep Alive" + } + }, + "type": "object", + "required": [ + "model", + "prompt" + ], + "title": "GenerateEmbeddingsForm" + }, + "GenerateImageForm": { + "properties": { + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model" + }, + "prompt": { + "type": "string", + "title": "Prompt" + }, + "size": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Size" + }, + "n": { + "type": "integer", + "title": "N", + "default": 1 + }, + "negative_prompt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Negative Prompt" + } + }, + "type": "object", + "required": [ + "prompt" + ], + "title": "GenerateImageForm" + }, + "GroupForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "permissions": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Permissions" + } + }, + "type": "object", + "required": [ + "name", + "description" + ], + "title": "GroupForm" + }, + "GroupResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "permissions": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Permissions" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "user_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "User Ids", + "default": [] + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "description", + "created_at", + "updated_at" + ], + "title": "GroupResponse" + }, + "GroupUpdateForm": { + "properties": { + "user_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "User Ids" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "permissions": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Permissions" + } + }, + "type": "object", + "required": [ + "name", + "description" + ], + "title": "GroupUpdateForm" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "ImageConfigForm": { + "properties": { + "MODEL": { + "type": "string", + "title": "Model" + }, + "IMAGE_SIZE": { + "type": "string", + "title": "Image Size" + }, + "IMAGE_STEPS": { + "type": "integer", + "title": "Image Steps" + } + }, + "type": "object", + "required": [ + "MODEL", + "IMAGE_SIZE", + "IMAGE_STEPS" + ], + "title": "ImageConfigForm" + }, + "ImportConfigForm": { + "properties": { + "config": { + "additionalProperties": true, + "type": "object", + "title": "Config" + } + }, + "type": "object", + "required": [ + "config" + ], + "title": "ImportConfigForm" + }, + "KnowledgeFileIdForm": { + "properties": { + "file_id": { + "type": "string", + "title": "File Id" + } + }, + "type": "object", + "required": [ + "file_id" + ], + "title": "KnowledgeFileIdForm" + }, + "KnowledgeFilesResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "files": { + "items": { + "$ref": "#/components/schemas/FileMetadataResponse" + }, + "type": "array", + "title": "Files" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "description", + "created_at", + "updated_at", + "files" + ], + "title": "KnowledgeFilesResponse" + }, + "KnowledgeForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "name", + "description" + ], + "title": "KnowledgeForm" + }, + "KnowledgeResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "files": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/FileMetadataResponse" + }, + { + "additionalProperties": true, + "type": "object" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Files" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "description", + "created_at", + "updated_at" + ], + "title": "KnowledgeResponse" + }, + "KnowledgeUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "description": { + "type": "string", + "title": "Description" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__models__users__UserResponse" + }, + { + "type": "null" + } + ] + }, + "files": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/FileMetadataResponse" + }, + { + "additionalProperties": true, + "type": "object" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Files" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "description", + "created_at", + "updated_at" + ], + "title": "KnowledgeUserResponse" + }, + "LdapConfigForm": { + "properties": { + "enable_ldap": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Ldap" + } + }, + "type": "object", + "title": "LdapConfigForm" + }, + "LdapForm": { + "properties": { + "user": { + "type": "string", + "title": "User" + }, + "password": { + "type": "string", + "title": "Password" + } + }, + "type": "object", + "required": [ + "user", + "password" + ], + "title": "LdapForm" + }, + "LdapServerConfig": { + "properties": { + "label": { + "type": "string", + "title": "Label" + }, + "host": { + "type": "string", + "title": "Host" + }, + "port": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Port" + }, + "attribute_for_mail": { + "type": "string", + "title": "Attribute For Mail", + "default": "mail" + }, + "attribute_for_username": { + "type": "string", + "title": "Attribute For Username", + "default": "uid" + }, + "app_dn": { + "type": "string", + "title": "App Dn" + }, + "app_dn_password": { + "type": "string", + "title": "App Dn Password" + }, + "search_base": { + "type": "string", + "title": "Search Base" + }, + "search_filters": { + "type": "string", + "title": "Search Filters", + "default": "" + }, + "use_tls": { + "type": "boolean", + "title": "Use Tls", + "default": true + }, + "certificate_path": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Certificate Path" + }, + "validate_cert": { + "type": "boolean", + "title": "Validate Cert", + "default": true + }, + "ciphers": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Ciphers", + "default": "ALL" + } + }, + "type": "object", + "required": [ + "label", + "host", + "app_dn", + "app_dn_password", + "search_base" + ], + "title": "LdapServerConfig" + }, + "LoadUrlForm": { + "properties": { + "url": { + "type": "string", + "maxLength": 2083, + "minLength": 1, + "format": "uri", + "title": "Url" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "LoadUrlForm" + }, + "MarkdownForm": { + "properties": { + "md": { + "type": "string", + "title": "Md" + } + }, + "type": "object", + "required": [ + "md" + ], + "title": "MarkdownForm" + }, + "MemoryModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "content": { + "type": "string", + "title": "Content" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "content", + "updated_at", + "created_at" + ], + "title": "MemoryModel" + }, + "MemoryUpdateModel": { + "properties": { + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content" + } + }, + "type": "object", + "title": "MemoryUpdateModel" + }, + "MessageModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "channel_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Channel Id" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "content": { + "type": "string", + "title": "Content" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "content", + "created_at", + "updated_at" + ], + "title": "MessageModel" + }, + "MessageUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "channel_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Channel Id" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "content": { + "type": "string", + "title": "Content" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "latest_reply_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Latest Reply At" + }, + "reply_count": { + "type": "integer", + "title": "Reply Count" + }, + "reactions": { + "items": { + "$ref": "#/components/schemas/Reactions" + }, + "type": "array", + "title": "Reactions" + }, + "user": { + "$ref": "#/components/schemas/UserNameResponse" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "content", + "created_at", + "updated_at", + "latest_reply_at", + "reply_count", + "reactions", + "user" + ], + "title": "MessageUserResponse" + }, + "ModelForm": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "base_model_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Model Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "meta": { + "$ref": "#/components/schemas/ModelMeta" + }, + "params": { + "$ref": "#/components/schemas/ModelParams" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "is_active": { + "type": "boolean", + "title": "Is Active", + "default": true + } + }, + "type": "object", + "required": [ + "id", + "name", + "meta", + "params" + ], + "title": "ModelForm" + }, + "ModelMeta": { + "properties": { + "profile_image_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Profile Image Url", + "default": "/static/favicon.png" + }, + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "capabilities": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Capabilities" + } + }, + "additionalProperties": true, + "type": "object", + "title": "ModelMeta" + }, + "ModelModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "base_model_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Model Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "params": { + "$ref": "#/components/schemas/ModelParams" + }, + "meta": { + "$ref": "#/components/schemas/ModelMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "params", + "meta", + "is_active", + "updated_at", + "created_at" + ], + "title": "ModelModel" + }, + "ModelNameForm": { + "properties": { + "model": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model" + } + }, + "additionalProperties": true, + "type": "object", + "title": "ModelNameForm" + }, + "ModelParams": { + "properties": {}, + "additionalProperties": true, + "type": "object", + "title": "ModelParams" + }, + "ModelResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "base_model_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Model Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "params": { + "$ref": "#/components/schemas/ModelParams" + }, + "meta": { + "$ref": "#/components/schemas/ModelMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "params", + "meta", + "is_active", + "updated_at", + "created_at" + ], + "title": "ModelResponse" + }, + "ModelUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "base_model_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Model Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "params": { + "$ref": "#/components/schemas/ModelParams" + }, + "meta": { + "$ref": "#/components/schemas/ModelMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "is_active": { + "type": "boolean", + "title": "Is Active" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__models__users__UserResponse" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "params", + "meta", + "is_active", + "updated_at", + "created_at" + ], + "title": "ModelUserResponse" + }, + "ModelsConfigForm": { + "properties": { + "DEFAULT_MODELS": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Default Models" + }, + "MODEL_ORDER_LIST": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Model Order List" + } + }, + "type": "object", + "required": [ + "DEFAULT_MODELS", + "MODEL_ORDER_LIST" + ], + "title": "ModelsConfigForm" + }, + "NoteForm": { + "properties": { + "title": { + "type": "string", + "title": "Title" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "title" + ], + "title": "NoteForm" + }, + "NoteModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "title", + "created_at", + "updated_at" + ], + "title": "NoteModel" + }, + "NoteTitleIdResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "title", + "updated_at", + "created_at" + ], + "title": "NoteTitleIdResponse" + }, + "NoteUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__models__users__UserResponse" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "title", + "created_at", + "updated_at" + ], + "title": "NoteUserResponse" + }, + "ProcessFileForm": { + "properties": { + "file_id": { + "type": "string", + "title": "File Id" + }, + "content": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content" + }, + "collection_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Collection Name" + } + }, + "type": "object", + "required": [ + "file_id" + ], + "title": "ProcessFileForm" + }, + "ProcessTextForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "content": { + "type": "string", + "title": "Content" + }, + "collection_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Collection Name" + } + }, + "type": "object", + "required": [ + "name", + "content" + ], + "title": "ProcessTextForm" + }, + "ProcessUrlForm": { + "properties": { + "collection_name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Collection Name" + }, + "url": { + "type": "string", + "title": "Url" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "ProcessUrlForm" + }, + "PromptForm": { + "properties": { + "command": { + "type": "string", + "title": "Command" + }, + "title": { + "type": "string", + "title": "Title" + }, + "content": { + "type": "string", + "title": "Content" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "command", + "title", + "content" + ], + "title": "PromptForm" + }, + "PromptModel": { + "properties": { + "command": { + "type": "string", + "title": "Command" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "content": { + "type": "string", + "title": "Content" + }, + "timestamp": { + "type": "integer", + "title": "Timestamp" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "command", + "user_id", + "title", + "content", + "timestamp" + ], + "title": "PromptModel" + }, + "PromptSuggestion": { + "properties": { + "title": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Title" + }, + "content": { + "type": "string", + "title": "Content" + } + }, + "type": "object", + "required": [ + "title", + "content" + ], + "title": "PromptSuggestion" + }, + "PromptUserResponse": { + "properties": { + "command": { + "type": "string", + "title": "Command" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "title": { + "type": "string", + "title": "Title" + }, + "content": { + "type": "string", + "title": "Content" + }, + "timestamp": { + "type": "integer", + "title": "Timestamp" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__models__users__UserResponse" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "command", + "user_id", + "title", + "content", + "timestamp" + ], + "title": "PromptUserResponse" + }, + "PushModelForm": { + "properties": { + "model": { + "type": "string", + "title": "Model" + }, + "insecure": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Insecure" + }, + "stream": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Stream" + } + }, + "type": "object", + "required": [ + "model" + ], + "title": "PushModelForm" + }, + "QueryCollectionsForm": { + "properties": { + "collection_names": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Collection Names" + }, + "query": { + "type": "string", + "title": "Query" + }, + "k": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "K" + }, + "k_reranker": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "K Reranker" + }, + "r": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "R" + }, + "hybrid": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Hybrid" + }, + "hybrid_bm25_weight": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Hybrid Bm25 Weight" + } + }, + "type": "object", + "required": [ + "collection_names", + "query" + ], + "title": "QueryCollectionsForm" + }, + "QueryDocForm": { + "properties": { + "collection_name": { + "type": "string", + "title": "Collection Name" + }, + "query": { + "type": "string", + "title": "Query" + }, + "k": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "K" + }, + "k_reranker": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "K Reranker" + }, + "r": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "R" + }, + "hybrid": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Hybrid" + } + }, + "type": "object", + "required": [ + "collection_name", + "query" + ], + "title": "QueryDocForm" + }, + "QueryMemoryForm": { + "properties": { + "content": { + "type": "string", + "title": "Content" + }, + "k": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "K", + "default": 1 + } + }, + "type": "object", + "required": [ + "content" + ], + "title": "QueryMemoryForm" + }, + "RatingData": { + "properties": { + "rating": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rating" + }, + "model_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Model Id" + }, + "sibling_model_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Sibling Model Ids" + }, + "reason": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Reason" + }, + "comment": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Comment" + } + }, + "additionalProperties": true, + "type": "object", + "title": "RatingData" + }, + "ReactionForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "ReactionForm" + }, + "Reactions": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "user_ids": { + "items": { + "type": "string" + }, + "type": "array", + "title": "User Ids" + }, + "count": { + "type": "integer", + "title": "Count" + } + }, + "type": "object", + "required": [ + "name", + "user_ids", + "count" + ], + "title": "Reactions" + }, + "STTConfigForm": { + "properties": { + "OPENAI_API_BASE_URL": { + "type": "string", + "title": "Openai Api Base Url" + }, + "OPENAI_API_KEY": { + "type": "string", + "title": "Openai Api Key" + }, + "ENGINE": { + "type": "string", + "title": "Engine" + }, + "MODEL": { + "type": "string", + "title": "Model" + }, + "SUPPORTED_CONTENT_TYPES": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Supported Content Types", + "default": [] + }, + "WHISPER_MODEL": { + "type": "string", + "title": "Whisper Model" + }, + "DEEPGRAM_API_KEY": { + "type": "string", + "title": "Deepgram Api Key" + }, + "AZURE_API_KEY": { + "type": "string", + "title": "Azure Api Key" + }, + "AZURE_REGION": { + "type": "string", + "title": "Azure Region" + }, + "AZURE_LOCALES": { + "type": "string", + "title": "Azure Locales" + }, + "AZURE_BASE_URL": { + "type": "string", + "title": "Azure Base Url" + }, + "AZURE_MAX_SPEAKERS": { + "type": "string", + "title": "Azure Max Speakers" + } + }, + "type": "object", + "required": [ + "OPENAI_API_BASE_URL", + "OPENAI_API_KEY", + "ENGINE", + "MODEL", + "WHISPER_MODEL", + "DEEPGRAM_API_KEY", + "AZURE_API_KEY", + "AZURE_REGION", + "AZURE_LOCALES", + "AZURE_BASE_URL", + "AZURE_MAX_SPEAKERS" + ], + "title": "STTConfigForm" + }, + "SearchForm": { + "properties": { + "queries": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Queries" + } + }, + "type": "object", + "required": [ + "queries" + ], + "title": "SearchForm" + }, + "SessionUserInfoResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "email": { + "type": "string", + "title": "Email" + }, + "name": { + "type": "string", + "title": "Name" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "token": { + "type": "string", + "title": "Token" + }, + "token_type": { + "type": "string", + "title": "Token Type" + }, + "expires_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Expires At" + }, + "permissions": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Permissions" + }, + "bio": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bio" + }, + "gender": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Gender" + }, + "date_of_birth": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Date Of Birth" + } + }, + "type": "object", + "required": [ + "id", + "email", + "name", + "role", + "profile_image_url", + "token", + "token_type" + ], + "title": "SessionUserInfoResponse" + }, + "SessionUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "email": { + "type": "string", + "title": "Email" + }, + "name": { + "type": "string", + "title": "Name" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "token": { + "type": "string", + "title": "Token" + }, + "token_type": { + "type": "string", + "title": "Token Type" + }, + "expires_at": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Expires At" + }, + "permissions": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Permissions" + } + }, + "type": "object", + "required": [ + "id", + "email", + "name", + "role", + "profile_image_url", + "token", + "token_type" + ], + "title": "SessionUserResponse" + }, + "SetBannersForm": { + "properties": { + "banners": { + "items": { + "$ref": "#/components/schemas/BannerModel" + }, + "type": "array", + "title": "Banners" + } + }, + "type": "object", + "required": [ + "banners" + ], + "title": "SetBannersForm" + }, + "SetDefaultSuggestionsForm": { + "properties": { + "suggestions": { + "items": { + "$ref": "#/components/schemas/PromptSuggestion" + }, + "type": "array", + "title": "Suggestions" + } + }, + "type": "object", + "required": [ + "suggestions" + ], + "title": "SetDefaultSuggestionsForm" + }, + "SharingPermissions": { + "properties": { + "public_models": { + "type": "boolean", + "title": "Public Models", + "default": true + }, + "public_knowledge": { + "type": "boolean", + "title": "Public Knowledge", + "default": true + }, + "public_prompts": { + "type": "boolean", + "title": "Public Prompts", + "default": true + }, + "public_tools": { + "type": "boolean", + "title": "Public Tools", + "default": true + } + }, + "type": "object", + "title": "SharingPermissions" + }, + "SigninForm": { + "properties": { + "email": { + "type": "string", + "title": "Email" + }, + "password": { + "type": "string", + "title": "Password" + } + }, + "type": "object", + "required": [ + "email", + "password" + ], + "title": "SigninForm" + }, + "SigninResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "email": { + "type": "string", + "title": "Email" + }, + "name": { + "type": "string", + "title": "Name" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "token": { + "type": "string", + "title": "Token" + }, + "token_type": { + "type": "string", + "title": "Token Type" + } + }, + "type": "object", + "required": [ + "id", + "email", + "name", + "role", + "profile_image_url", + "token", + "token_type" + ], + "title": "SigninResponse" + }, + "SignupForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "password": { + "type": "string", + "title": "Password" + }, + "profile_image_url": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Profile Image Url", + "default": "/user.png" + } + }, + "type": "object", + "required": [ + "name", + "email", + "password" + ], + "title": "SignupForm" + }, + "SnapshotData": { + "properties": { + "chat": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Chat" + } + }, + "additionalProperties": true, + "type": "object", + "title": "SnapshotData" + }, + "SyncFunctionsForm": { + "properties": { + "functions": { + "items": { + "$ref": "#/components/schemas/FunctionWithValvesModel" + }, + "type": "array", + "title": "Functions", + "default": [] + } + }, + "type": "object", + "title": "SyncFunctionsForm" + }, + "SyncModelsForm": { + "properties": { + "models": { + "items": { + "$ref": "#/components/schemas/ModelModel" + }, + "type": "array", + "title": "Models", + "default": [] + } + }, + "type": "object", + "title": "SyncModelsForm" + }, + "TTSConfigForm": { + "properties": { + "OPENAI_API_BASE_URL": { + "type": "string", + "title": "Openai Api Base Url" + }, + "OPENAI_API_KEY": { + "type": "string", + "title": "Openai Api Key" + }, + "API_KEY": { + "type": "string", + "title": "Api Key" + }, + "ENGINE": { + "type": "string", + "title": "Engine" + }, + "MODEL": { + "type": "string", + "title": "Model" + }, + "VOICE": { + "type": "string", + "title": "Voice" + }, + "SPLIT_ON": { + "type": "string", + "title": "Split On" + }, + "AZURE_SPEECH_REGION": { + "type": "string", + "title": "Azure Speech Region" + }, + "AZURE_SPEECH_BASE_URL": { + "type": "string", + "title": "Azure Speech Base Url" + }, + "AZURE_SPEECH_OUTPUT_FORMAT": { + "type": "string", + "title": "Azure Speech Output Format" + } + }, + "type": "object", + "required": [ + "OPENAI_API_BASE_URL", + "OPENAI_API_KEY", + "API_KEY", + "ENGINE", + "MODEL", + "VOICE", + "SPLIT_ON", + "AZURE_SPEECH_REGION", + "AZURE_SPEECH_BASE_URL", + "AZURE_SPEECH_OUTPUT_FORMAT" + ], + "title": "TTSConfigForm" + }, + "TagFilterForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "skip": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Skip", + "default": 0 + }, + "limit": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Limit", + "default": 50 + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "TagFilterForm" + }, + "TagForm": { + "properties": { + "name": { + "type": "string", + "title": "Name" + } + }, + "type": "object", + "required": [ + "name" + ], + "title": "TagForm" + }, + "TagModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + } + }, + "type": "object", + "required": [ + "id", + "name", + "user_id" + ], + "title": "TagModel" + }, + "TaskConfigForm": { + "properties": { + "TASK_MODEL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Task Model" + }, + "TASK_MODEL_EXTERNAL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Task Model External" + }, + "ENABLE_TITLE_GENERATION": { + "type": "boolean", + "title": "Enable Title Generation" + }, + "TITLE_GENERATION_PROMPT_TEMPLATE": { + "type": "string", + "title": "Title Generation Prompt Template" + }, + "IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE": { + "type": "string", + "title": "Image Prompt Generation Prompt Template" + }, + "ENABLE_AUTOCOMPLETE_GENERATION": { + "type": "boolean", + "title": "Enable Autocomplete Generation" + }, + "AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH": { + "type": "integer", + "title": "Autocomplete Generation Input Max Length" + }, + "TAGS_GENERATION_PROMPT_TEMPLATE": { + "type": "string", + "title": "Tags Generation Prompt Template" + }, + "FOLLOW_UP_GENERATION_PROMPT_TEMPLATE": { + "type": "string", + "title": "Follow Up Generation Prompt Template" + }, + "ENABLE_FOLLOW_UP_GENERATION": { + "type": "boolean", + "title": "Enable Follow Up Generation" + }, + "ENABLE_TAGS_GENERATION": { + "type": "boolean", + "title": "Enable Tags Generation" + }, + "ENABLE_SEARCH_QUERY_GENERATION": { + "type": "boolean", + "title": "Enable Search Query Generation" + }, + "ENABLE_RETRIEVAL_QUERY_GENERATION": { + "type": "boolean", + "title": "Enable Retrieval Query Generation" + }, + "QUERY_GENERATION_PROMPT_TEMPLATE": { + "type": "string", + "title": "Query Generation Prompt Template" + }, + "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE": { + "type": "string", + "title": "Tools Function Calling Prompt Template" + } + }, + "type": "object", + "required": [ + "TASK_MODEL", + "TASK_MODEL_EXTERNAL", + "ENABLE_TITLE_GENERATION", + "TITLE_GENERATION_PROMPT_TEMPLATE", + "IMAGE_PROMPT_GENERATION_PROMPT_TEMPLATE", + "ENABLE_AUTOCOMPLETE_GENERATION", + "AUTOCOMPLETE_GENERATION_INPUT_MAX_LENGTH", + "TAGS_GENERATION_PROMPT_TEMPLATE", + "FOLLOW_UP_GENERATION_PROMPT_TEMPLATE", + "ENABLE_FOLLOW_UP_GENERATION", + "ENABLE_TAGS_GENERATION", + "ENABLE_SEARCH_QUERY_GENERATION", + "ENABLE_RETRIEVAL_QUERY_GENERATION", + "QUERY_GENERATION_PROMPT_TEMPLATE", + "TOOLS_FUNCTION_CALLING_PROMPT_TEMPLATE" + ], + "title": "TaskConfigForm" + }, + "ToolForm": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "content": { + "type": "string", + "title": "Content" + }, + "meta": { + "$ref": "#/components/schemas/ToolMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + } + }, + "type": "object", + "required": [ + "id", + "name", + "content", + "meta" + ], + "title": "ToolForm" + }, + "ToolMeta": { + "properties": { + "description": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Description" + }, + "manifest": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Manifest", + "default": {} + } + }, + "type": "object", + "title": "ToolMeta" + }, + "ToolModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "content": { + "type": "string", + "title": "Content" + }, + "specs": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array", + "title": "Specs" + }, + "meta": { + "$ref": "#/components/schemas/ToolMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "content", + "specs", + "meta", + "updated_at", + "created_at" + ], + "title": "ToolModel" + }, + "ToolResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "meta": { + "$ref": "#/components/schemas/ToolMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "meta", + "updated_at", + "created_at" + ], + "title": "ToolResponse" + }, + "ToolServerConnection": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "path": { + "type": "string", + "title": "Path" + }, + "auth_type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Auth Type" + }, + "key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Key" + }, + "config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Config" + } + }, + "additionalProperties": true, + "type": "object", + "required": [ + "url", + "path", + "auth_type", + "key", + "config" + ], + "title": "ToolServerConnection" + }, + "ToolServersConfigForm": { + "properties": { + "TOOL_SERVER_CONNECTIONS": { + "items": { + "$ref": "#/components/schemas/ToolServerConnection" + }, + "type": "array", + "title": "Tool Server Connections" + } + }, + "type": "object", + "required": [ + "TOOL_SERVER_CONNECTIONS" + ], + "title": "ToolServersConfigForm" + }, + "ToolUserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "user_id": { + "type": "string", + "title": "User Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "meta": { + "$ref": "#/components/schemas/ToolMeta" + }, + "access_control": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Access Control" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/open_webui__models__users__UserResponse" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "user_id", + "name", + "meta", + "updated_at", + "created_at" + ], + "title": "ToolUserResponse" + }, + "UpdateConfigForm": { + "properties": { + "ENABLE_EVALUATION_ARENA_MODELS": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Evaluation Arena Models" + }, + "EVALUATION_ARENA_MODELS": { + "anyOf": [ + { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Evaluation Arena Models" + } + }, + "type": "object", + "title": "UpdateConfigForm" + }, + "UpdatePasswordForm": { + "properties": { + "password": { + "type": "string", + "title": "Password" + }, + "new_password": { + "type": "string", + "title": "New Password" + } + }, + "type": "object", + "required": [ + "password", + "new_password" + ], + "title": "UpdatePasswordForm" + }, + "UpdateProfileForm": { + "properties": { + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "name": { + "type": "string", + "title": "Name" + }, + "bio": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bio" + }, + "gender": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Gender" + }, + "date_of_birth": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Date Of Birth" + } + }, + "type": "object", + "required": [ + "profile_image_url", + "name" + ], + "title": "UpdateProfileForm" + }, + "UrlForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "UrlForm" + }, + "UserIdsForm": { + "properties": { + "user_ids": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "User Ids" + } + }, + "type": "object", + "title": "UserIdsForm" + }, + "UserInfoListResponse": { + "properties": { + "users": { + "items": { + "$ref": "#/components/schemas/UserInfoResponse" + }, + "type": "array", + "title": "Users" + }, + "total": { + "type": "integer", + "title": "Total" + } + }, + "type": "object", + "required": [ + "users", + "total" + ], + "title": "UserInfoListResponse" + }, + "UserInfoResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "role": { + "type": "string", + "title": "Role" + } + }, + "type": "object", + "required": [ + "id", + "name", + "email", + "role" + ], + "title": "UserInfoResponse" + }, + "UserListResponse": { + "properties": { + "users": { + "items": { + "$ref": "#/components/schemas/UserModel" + }, + "type": "array", + "title": "Users" + }, + "total": { + "type": "integer", + "title": "Total" + } + }, + "type": "object", + "required": [ + "users", + "total" + ], + "title": "UserListResponse" + }, + "UserModel": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "username": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Username" + }, + "role": { + "type": "string", + "title": "Role", + "default": "pending" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "bio": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bio" + }, + "gender": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Gender" + }, + "date_of_birth": { + "anyOf": [ + { + "type": "string", + "format": "date" + }, + { + "type": "null" + } + ], + "title": "Date Of Birth" + }, + "info": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Info" + }, + "settings": { + "anyOf": [ + { + "$ref": "#/components/schemas/UserSettings" + }, + { + "type": "null" + } + ] + }, + "api_key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Key" + }, + "oauth_sub": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Oauth Sub" + }, + "last_active_at": { + "type": "integer", + "title": "Last Active At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "email", + "profile_image_url", + "last_active_at", + "updated_at", + "created_at" + ], + "title": "UserModel" + }, + "UserNameResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + } + }, + "type": "object", + "required": [ + "id", + "name", + "role", + "profile_image_url" + ], + "title": "UserNameResponse" + }, + "UserPermissions": { + "properties": { + "workspace": { + "$ref": "#/components/schemas/WorkspacePermissions" + }, + "sharing": { + "$ref": "#/components/schemas/SharingPermissions" + }, + "chat": { + "$ref": "#/components/schemas/ChatPermissions" + }, + "features": { + "$ref": "#/components/schemas/FeaturesPermissions" + } + }, + "type": "object", + "required": [ + "workspace", + "sharing", + "chat", + "features" + ], + "title": "UserPermissions" + }, + "UserSettings": { + "properties": { + "ui": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Ui", + "default": {} + } + }, + "additionalProperties": true, + "type": "object", + "title": "UserSettings" + }, + "UserUpdateForm": { + "properties": { + "role": { + "type": "string", + "title": "Role" + }, + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "password": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Password" + } + }, + "type": "object", + "required": [ + "role", + "name", + "email", + "profile_image_url" + ], + "title": "UserUpdateForm" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "WebConfig": { + "properties": { + "ENABLE_WEB_SEARCH": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Web Search" + }, + "WEB_SEARCH_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Web Search Engine" + }, + "WEB_SEARCH_TRUST_ENV": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Web Search Trust Env" + }, + "WEB_SEARCH_RESULT_COUNT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Web Search Result Count" + }, + "WEB_SEARCH_CONCURRENT_REQUESTS": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Web Search Concurrent Requests" + }, + "WEB_LOADER_CONCURRENT_REQUESTS": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Web Loader Concurrent Requests" + }, + "WEB_SEARCH_DOMAIN_FILTER_LIST": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Web Search Domain Filter List", + "default": [] + }, + "BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Bypass Web Search Embedding And Retrieval" + }, + "BYPASS_WEB_SEARCH_WEB_LOADER": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Bypass Web Search Web Loader" + }, + "SEARXNG_QUERY_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Searxng Query Url" + }, + "YACY_QUERY_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Yacy Query Url" + }, + "YACY_USERNAME": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Yacy Username" + }, + "YACY_PASSWORD": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Yacy Password" + }, + "GOOGLE_PSE_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Google Pse Api Key" + }, + "GOOGLE_PSE_ENGINE_ID": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Google Pse Engine Id" + }, + "BRAVE_SEARCH_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Brave Search Api Key" + }, + "KAGI_SEARCH_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Kagi Search Api Key" + }, + "MOJEEK_SEARCH_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Mojeek Search Api Key" + }, + "BOCHA_SEARCH_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bocha Search Api Key" + }, + "SERPSTACK_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Serpstack Api Key" + }, + "SERPSTACK_HTTPS": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Serpstack Https" + }, + "SERPER_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Serper Api Key" + }, + "SERPLY_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Serply Api Key" + }, + "TAVILY_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tavily Api Key" + }, + "SEARCHAPI_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Searchapi Api Key" + }, + "SEARCHAPI_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Searchapi Engine" + }, + "SERPAPI_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Serpapi Api Key" + }, + "SERPAPI_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Serpapi Engine" + }, + "JINA_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Jina Api Key" + }, + "BING_SEARCH_V7_ENDPOINT": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bing Search V7 Endpoint" + }, + "BING_SEARCH_V7_SUBSCRIPTION_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Bing Search V7 Subscription Key" + }, + "EXA_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Exa Api Key" + }, + "PERPLEXITY_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Perplexity Api Key" + }, + "PERPLEXITY_MODEL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Perplexity Model" + }, + "PERPLEXITY_SEARCH_CONTEXT_USAGE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Perplexity Search Context Usage" + }, + "SOUGOU_API_SID": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Sougou Api Sid" + }, + "SOUGOU_API_SK": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Sougou Api Sk" + }, + "WEB_LOADER_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Web Loader Engine" + }, + "ENABLE_WEB_LOADER_SSL_VERIFICATION": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Web Loader Ssl Verification" + }, + "PLAYWRIGHT_WS_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Playwright Ws Url" + }, + "PLAYWRIGHT_TIMEOUT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Playwright Timeout" + }, + "FIRECRAWL_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Firecrawl Api Key" + }, + "FIRECRAWL_API_BASE_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Firecrawl Api Base Url" + }, + "TAVILY_EXTRACT_DEPTH": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tavily Extract Depth" + }, + "EXTERNAL_WEB_SEARCH_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Web Search Url" + }, + "EXTERNAL_WEB_SEARCH_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Web Search Api Key" + }, + "EXTERNAL_WEB_LOADER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Web Loader Url" + }, + "EXTERNAL_WEB_LOADER_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Web Loader Api Key" + }, + "YOUTUBE_LOADER_LANGUAGE": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Youtube Loader Language" + }, + "YOUTUBE_LOADER_PROXY_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Youtube Loader Proxy Url" + }, + "YOUTUBE_LOADER_TRANSLATION": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Youtube Loader Translation" + } + }, + "type": "object", + "title": "WebConfig" + }, + "WorkspacePermissions": { + "properties": { + "models": { + "type": "boolean", + "title": "Models", + "default": false + }, + "knowledge": { + "type": "boolean", + "title": "Knowledge", + "default": false + }, + "prompts": { + "type": "boolean", + "title": "Prompts", + "default": false + }, + "tools": { + "type": "boolean", + "title": "Tools", + "default": false + } + }, + "type": "object", + "title": "WorkspacePermissions" + }, + "open_webui__models__auths__UserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "email": { + "type": "string", + "title": "Email" + }, + "name": { + "type": "string", + "title": "Name" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + } + }, + "type": "object", + "required": [ + "id", + "email", + "name", + "role", + "profile_image_url" + ], + "title": "UserResponse" + }, + "open_webui__models__messages__MessageForm": { + "properties": { + "content": { + "type": "string", + "title": "Content" + }, + "parent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Parent Id" + }, + "data": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Data" + }, + "meta": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Meta" + } + }, + "type": "object", + "required": [ + "content" + ], + "title": "MessageForm" + }, + "open_webui__models__users__UserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "role": { + "type": "string", + "title": "Role" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + } + }, + "type": "object", + "required": [ + "id", + "name", + "email", + "role", + "profile_image_url" + ], + "title": "UserResponse" + }, + "open_webui__routers__chats__MessageForm": { + "properties": { + "content": { + "type": "string", + "title": "Content" + } + }, + "type": "object", + "required": [ + "content" + ], + "title": "MessageForm" + }, + "open_webui__routers__evaluations__UserResponse": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "name": { + "type": "string", + "title": "Name" + }, + "email": { + "type": "string", + "title": "Email" + }, + "role": { + "type": "string", + "title": "Role", + "default": "pending" + }, + "last_active_at": { + "type": "integer", + "title": "Last Active At" + }, + "updated_at": { + "type": "integer", + "title": "Updated At" + }, + "created_at": { + "type": "integer", + "title": "Created At" + } + }, + "type": "object", + "required": [ + "id", + "name", + "email", + "last_active_at", + "updated_at", + "created_at" + ], + "title": "UserResponse" + }, + "open_webui__routers__images__ConfigForm": { + "properties": { + "enabled": { + "type": "boolean", + "title": "Enabled" + }, + "engine": { + "type": "string", + "title": "Engine" + }, + "prompt_generation": { + "type": "boolean", + "title": "Prompt Generation" + }, + "openai": { + "$ref": "#/components/schemas/open_webui__routers__images__OpenAIConfigForm" + }, + "automatic1111": { + "$ref": "#/components/schemas/Automatic1111ConfigForm" + }, + "comfyui": { + "$ref": "#/components/schemas/ComfyUIConfigForm" + }, + "gemini": { + "$ref": "#/components/schemas/GeminiConfigForm" + } + }, + "type": "object", + "required": [ + "enabled", + "engine", + "prompt_generation", + "openai", + "automatic1111", + "comfyui", + "gemini" + ], + "title": "ConfigForm" + }, + "open_webui__routers__images__OpenAIConfigForm": { + "properties": { + "OPENAI_API_BASE_URL": { + "type": "string", + "title": "Openai Api Base Url" + }, + "OPENAI_API_VERSION": { + "type": "string", + "title": "Openai Api Version" + }, + "OPENAI_API_KEY": { + "type": "string", + "title": "Openai Api Key" + } + }, + "type": "object", + "required": [ + "OPENAI_API_BASE_URL", + "OPENAI_API_VERSION", + "OPENAI_API_KEY" + ], + "title": "OpenAIConfigForm" + }, + "open_webui__routers__ollama__ConnectionVerificationForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "key": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Key" + } + }, + "type": "object", + "required": [ + "url" + ], + "title": "ConnectionVerificationForm" + }, + "open_webui__routers__ollama__OllamaConfigForm": { + "properties": { + "ENABLE_OLLAMA_API": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Ollama Api" + }, + "OLLAMA_BASE_URLS": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Ollama Base Urls" + }, + "OLLAMA_API_CONFIGS": { + "additionalProperties": true, + "type": "object", + "title": "Ollama Api Configs" + } + }, + "type": "object", + "required": [ + "OLLAMA_BASE_URLS", + "OLLAMA_API_CONFIGS" + ], + "title": "OllamaConfigForm" + }, + "open_webui__routers__openai__ConnectionVerificationForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "key": { + "type": "string", + "title": "Key" + }, + "config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Config" + } + }, + "type": "object", + "required": [ + "url", + "key" + ], + "title": "ConnectionVerificationForm" + }, + "open_webui__routers__openai__OpenAIConfigForm": { + "properties": { + "ENABLE_OPENAI_API": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Openai Api" + }, + "OPENAI_API_BASE_URLS": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Openai Api Base Urls" + }, + "OPENAI_API_KEYS": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Openai Api Keys" + }, + "OPENAI_API_CONFIGS": { + "additionalProperties": true, + "type": "object", + "title": "Openai Api Configs" + } + }, + "type": "object", + "required": [ + "OPENAI_API_BASE_URLS", + "OPENAI_API_KEYS", + "OPENAI_API_CONFIGS" + ], + "title": "OpenAIConfigForm" + }, + "open_webui__routers__retrieval__ConfigForm": { + "properties": { + "RAG_TEMPLATE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rag Template" + }, + "TOP_K": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Top K" + }, + "BYPASS_EMBEDDING_AND_RETRIEVAL": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Bypass Embedding And Retrieval" + }, + "RAG_FULL_CONTEXT": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Rag Full Context" + }, + "ENABLE_RAG_HYBRID_SEARCH": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Rag Hybrid Search" + }, + "TOP_K_RERANKER": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Top K Reranker" + }, + "RELEVANCE_THRESHOLD": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Relevance Threshold" + }, + "HYBRID_BM25_WEIGHT": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Hybrid Bm25 Weight" + }, + "CONTENT_EXTRACTION_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Content Extraction Engine" + }, + "PDF_EXTRACT_IMAGES": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Pdf Extract Images" + }, + "DATALAB_MARKER_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Api Key" + }, + "DATALAB_MARKER_API_BASE_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Api Base Url" + }, + "DATALAB_MARKER_ADDITIONAL_CONFIG": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Additional Config" + }, + "DATALAB_MARKER_SKIP_CACHE": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Skip Cache" + }, + "DATALAB_MARKER_FORCE_OCR": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Force Ocr" + }, + "DATALAB_MARKER_PAGINATE": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Paginate" + }, + "DATALAB_MARKER_STRIP_EXISTING_OCR": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Strip Existing Ocr" + }, + "DATALAB_MARKER_DISABLE_IMAGE_EXTRACTION": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Disable Image Extraction" + }, + "DATALAB_MARKER_FORMAT_LINES": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Format Lines" + }, + "DATALAB_MARKER_USE_LLM": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Use Llm" + }, + "DATALAB_MARKER_OUTPUT_FORMAT": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Datalab Marker Output Format" + }, + "EXTERNAL_DOCUMENT_LOADER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Document Loader Url" + }, + "EXTERNAL_DOCUMENT_LOADER_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "External Document Loader Api Key" + }, + "TIKA_SERVER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Tika Server Url" + }, + "DOCLING_SERVER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Server Url" + }, + "DOCLING_DO_OCR": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Docling Do Ocr" + }, + "DOCLING_FORCE_OCR": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Docling Force Ocr" + }, + "DOCLING_OCR_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Ocr Engine" + }, + "DOCLING_OCR_LANG": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Ocr Lang" + }, + "DOCLING_PDF_BACKEND": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Pdf Backend" + }, + "DOCLING_TABLE_MODE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Table Mode" + }, + "DOCLING_PIPELINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Pipeline" + }, + "DOCLING_DO_PICTURE_DESCRIPTION": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Docling Do Picture Description" + }, + "DOCLING_PICTURE_DESCRIPTION_MODE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Docling Picture Description Mode" + }, + "DOCLING_PICTURE_DESCRIPTION_LOCAL": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Docling Picture Description Local" + }, + "DOCLING_PICTURE_DESCRIPTION_API": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Docling Picture Description Api" + }, + "DOCUMENT_INTELLIGENCE_ENDPOINT": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Intelligence Endpoint" + }, + "DOCUMENT_INTELLIGENCE_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Document Intelligence Key" + }, + "MISTRAL_OCR_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Mistral Ocr Api Key" + }, + "RAG_RERANKING_MODEL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rag Reranking Model" + }, + "RAG_RERANKING_ENGINE": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rag Reranking Engine" + }, + "RAG_EXTERNAL_RERANKER_URL": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rag External Reranker Url" + }, + "RAG_EXTERNAL_RERANKER_API_KEY": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Rag External Reranker Api Key" + }, + "TEXT_SPLITTER": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Text Splitter" + }, + "CHUNK_SIZE": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Chunk Size" + }, + "CHUNK_OVERLAP": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Chunk Overlap" + }, + "FILE_MAX_SIZE": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Max Size" + }, + "FILE_MAX_COUNT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Max Count" + }, + "FILE_IMAGE_COMPRESSION_WIDTH": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Image Compression Width" + }, + "FILE_IMAGE_COMPRESSION_HEIGHT": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "File Image Compression Height" + }, + "ALLOWED_FILE_EXTENSIONS": { + "anyOf": [ + { + "items": { + "type": "string" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Allowed File Extensions" + }, + "ENABLE_GOOGLE_DRIVE_INTEGRATION": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Google Drive Integration" + }, + "ENABLE_ONEDRIVE_INTEGRATION": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Enable Onedrive Integration" + }, + "web": { + "anyOf": [ + { + "$ref": "#/components/schemas/WebConfig" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "title": "ConfigForm" + }, + "open_webui__routers__retrieval__OllamaConfigForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "key": { + "type": "string", + "title": "Key" + } + }, + "type": "object", + "required": [ + "url", + "key" + ], + "title": "OllamaConfigForm" + }, + "open_webui__routers__retrieval__OpenAIConfigForm": { + "properties": { + "url": { + "type": "string", + "title": "Url" + }, + "key": { + "type": "string", + "title": "Key" + } + }, + "type": "object", + "required": [ + "url", + "key" + ], + "title": "OpenAIConfigForm" + }, + "open_webui__routers__users__UserResponse": { + "properties": { + "name": { + "type": "string", + "title": "Name" + }, + "profile_image_url": { + "type": "string", + "title": "Profile Image Url" + }, + "active": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "title": "Active" + } + }, + "type": "object", + "required": [ + "name", + "profile_image_url" + ], + "title": "UserResponse" + } + }, + "securitySchemes": { + "HTTPBearer": { + "type": "http", + "scheme": "bearer" + } + } + } +} \ No newline at end of file diff --git a/ingest_pipeline/automations/__init__.py b/ingest_pipeline/automations/__init__.py new file mode 100644 index 0000000..b50ca00 --- /dev/null +++ b/ingest_pipeline/automations/__init__.py @@ -0,0 +1,62 @@ +"""Prefect Automations for ingestion pipeline monitoring and management.""" + +# Automation configurations as YAML-ready dictionaries +AUTOMATION_TEMPLATES = { + "cancel_long_running": """ +name: Cancel Long Running Ingestion Flows +description: Cancels ingestion flows running longer than 30 minutes +trigger: + type: event + posture: Proactive + expect: [prefect.flow-run.Running] + match_related: + prefect.resource.role: flow + prefect.resource.name: ingestion_pipeline + threshold: 1 + within: 1800 +actions: + - type: cancel-flow-run + source: inferred +enabled: true +""", + + "retry_failed": """ +name: Retry Failed Ingestion Flows +description: Retries failed ingestion flows with original parameters +trigger: + type: event + posture: Reactive + expect: [prefect.flow-run.Failed] + match_related: + prefect.resource.role: flow + prefect.resource.name: ingestion_pipeline + threshold: 1 + within: 0 +actions: + - type: run-deployment + source: inferred + parameters: + validate_first: false +enabled: true +""", + + "resource_monitoring": """ +name: Manage Work Pool Based on Resources +description: Pauses work pool when system resources are constrained +trigger: + type: event + posture: Reactive + expect: [system.resource.high_usage] + threshold: 1 + within: 120 +actions: + - type: pause-work-pool + work_pool_name: default +enabled: true +""", +} + + +def get_automation_yaml_templates() -> dict[str, str]: + """Get automation templates as YAML strings.""" + return AUTOMATION_TEMPLATES.copy() diff --git a/ingest_pipeline/cli/__pycache__/main.cpython-312.pyc b/ingest_pipeline/cli/__pycache__/main.cpython-312.pyc index 74efecb..c0fabe5 100644 Binary files a/ingest_pipeline/cli/__pycache__/main.cpython-312.pyc and b/ingest_pipeline/cli/__pycache__/main.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/main.py b/ingest_pipeline/cli/main.py index 6a99fce..9022ad8 100644 --- a/ingest_pipeline/cli/main.py +++ b/ingest_pipeline/cli/main.py @@ -4,13 +4,19 @@ import asyncio from typing import Annotated import typer +from pydantic import SecretStr from rich.console import Console from rich.panel import Panel from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn, TextColumn from rich.table import Table from ..config import configure_prefect, get_settings -from ..core.models import IngestionResult, IngestionSource, StorageBackend +from ..core.models import ( + IngestionResult, + IngestionSource, + StorageBackend, + StorageConfig, +) from ..flows.ingestion import create_ingestion_flow from ..flows.scheduler import create_scheduled_deployment, serve_deployments @@ -439,6 +445,22 @@ def search( asyncio.run(run_search(query, collection, backend.value, limit)) +@app.command(name="blocks") +def blocks_command() -> None: + """🧩 List and manage Prefect Blocks.""" + console.print("[bold cyan]📦 Prefect Blocks Management[/bold cyan]") + console.print("Use 'prefect block register --module ingest_pipeline.core.models' to register custom blocks") + console.print("Use 'prefect block ls' to list available blocks") + + +@app.command(name="variables") +def variables_command() -> None: + """📊 Manage Prefect Variables.""" + console.print("[bold cyan]📊 Prefect Variables Management[/bold cyan]") + console.print("Use 'prefect variable set VARIABLE_NAME value' to set variables") + console.print("Use 'prefect variable ls' to list variables") + + async def run_ingestion( url: str, source_type: IngestionSource, @@ -470,8 +492,8 @@ async def run_list_collections() -> None: """ List collections across storage backends. """ - from ..config import configure_prefect, get_settings - from ..core.models import StorageBackend, StorageConfig + from ..config import get_settings + from ..core.models import StorageBackend from ..storage.openwebui import OpenWebUIStorage from ..storage.weaviate import WeaviateStorage @@ -485,7 +507,7 @@ async def run_list_collections() -> None: weaviate_config = StorageConfig( backend=StorageBackend.WEAVIATE, endpoint=settings.weaviate_endpoint, - api_key=settings.weaviate_api_key, + api_key=SecretStr(settings.weaviate_api_key) if settings.weaviate_api_key is not None else None, collection_name="default", ) weaviate = WeaviateStorage(weaviate_config) @@ -494,7 +516,8 @@ async def run_list_collections() -> None: overview = await weaviate.describe_collections() for item in overview: name = str(item.get("name", "Unknown")) - count = int(item.get("count", 0)) + count_val = item.get("count", 0) + count = int(count_val) if isinstance(count_val, (int, str)) else 0 weaviate_collections.append((name, count)) except Exception as e: console.print(f"❌ [red]Weaviate connection failed: {e}[/red]") @@ -505,7 +528,7 @@ async def run_list_collections() -> None: openwebui_config = StorageConfig( backend=StorageBackend.OPEN_WEBUI, endpoint=settings.openwebui_endpoint, - api_key=settings.openwebui_api_key, + api_key=SecretStr(settings.openwebui_api_key) if settings.openwebui_api_key is not None else None, collection_name="default", ) openwebui = OpenWebUIStorage(openwebui_config) @@ -514,7 +537,8 @@ async def run_list_collections() -> None: overview = await openwebui.describe_collections() for item in overview: name = str(item.get("name", "Unknown")) - count = int(item.get("count", 0)) + count_val = item.get("count", 0) + count = int(count_val) if isinstance(count_val, (int, str)) else 0 openwebui_collections.append((name, count)) except Exception as e: console.print(f"❌ [red]OpenWebUI connection failed: {e}[/red]") @@ -551,8 +575,8 @@ async def run_search(query: str, collection: str | None, backend: str, limit: in """ Search across collections. """ - from ..config import configure_prefect, get_settings - from ..core.models import StorageBackend, StorageConfig + from ..config import get_settings + from ..core.models import StorageBackend from ..storage.weaviate import WeaviateStorage settings = get_settings() @@ -569,7 +593,7 @@ async def run_search(query: str, collection: str | None, backend: str, limit: in weaviate_config = StorageConfig( backend=StorageBackend.WEAVIATE, endpoint=settings.weaviate_endpoint, - api_key=settings.weaviate_api_key, + api_key=SecretStr(settings.weaviate_api_key) if settings.weaviate_api_key is not None else None, collection_name=collection or "default", ) weaviate = WeaviateStorage(weaviate_config) diff --git a/ingest_pipeline/cli/tui/__pycache__/app.cpython-312.pyc b/ingest_pipeline/cli/tui/__pycache__/app.cpython-312.pyc index 691b082..fd540df 100644 Binary files a/ingest_pipeline/cli/tui/__pycache__/app.cpython-312.pyc and b/ingest_pipeline/cli/tui/__pycache__/app.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc b/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc index 53a0865..4530492 100644 Binary files a/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc and b/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/app.py b/ingest_pipeline/cli/tui/app.py index 39ed963..3e6651d 100644 --- a/ingest_pipeline/cli/tui/app.py +++ b/ingest_pipeline/cli/tui/app.py @@ -17,7 +17,8 @@ from textual.timer import Timer from ...storage.base import BaseStorage from ...storage.openwebui import OpenWebUIStorage from ...storage.weaviate import WeaviateStorage -from .screens import CollectionOverviewScreen, HelpScreen +from .screens.dashboard import CollectionOverviewScreen +from .screens.help import HelpScreen from .styles import TUI_CSS from .utils.storage_manager import StorageManager @@ -35,6 +36,8 @@ class CollectionManagementApp(App[None]): """Enhanced modern Textual application with comprehensive keyboard navigation.""" CSS: ClassVar[str] = TUI_CSS + TITLE = "Collection Management" + SUB_TITLE = "Document Ingestion Pipeline" def safe_notify( self, @@ -89,8 +92,8 @@ class CollectionManagementApp(App[None]): self.weaviate = weaviate self.openwebui = openwebui self.r2r = r2r - self.title: str = "" - self.sub_title: str = "" + # Remove direct assignment to read-only title properties + # These should be set through class attributes or overridden methods self.log_queue = log_queue self._log_formatter = log_formatter or logging.Formatter( fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", @@ -146,7 +149,7 @@ class CollectionManagementApp(App[None]): if drained and self._log_viewer is not None: self._log_viewer.append_logs(drained) - def attach_log_viewer(self, viewer: "LogViewerScreen") -> None: + def attach_log_viewer(self, viewer: LogViewerScreen) -> None: """Register an active log viewer and hydrate it with existing entries.""" self._log_viewer = viewer viewer.replace_logs(list(self._log_buffer)) @@ -154,7 +157,7 @@ class CollectionManagementApp(App[None]): # Drain once more to deliver any entries gathered between instantiation and mount self._drain_log_queue() - def detach_log_viewer(self, viewer: "LogViewerScreen") -> None: + def detach_log_viewer(self, viewer: LogViewerScreen) -> None: """Remove the current log viewer when it is dismissed.""" if self._log_viewer is viewer: self._log_viewer = None @@ -273,7 +276,7 @@ class CollectionManagementApp(App[None]): if len(self.screen_stack) > 1: # Don't close the main screen _ = self.pop_screen() else: - _ = self.notify("Cannot close main screen. Use Q to quit.", severity="warning") + self.notify("Cannot close main screen. Use Q to quit.", severity="warning") def action_dashboard_tab(self) -> None: """Switch to dashboard tab in current screen.""" @@ -305,7 +308,7 @@ class CollectionManagementApp(App[None]): _ = event.prevent_default() elif event.key == "ctrl+alt+r": # Force refresh all connections - _ = self.notify("🔄 Refreshing all connections...", severity="information") + self.notify("🔄 Refreshing all connections...", severity="information") # This could trigger a full reinit if needed _ = event.prevent_default() # No else clause needed - just handle our events diff --git a/ingest_pipeline/cli/tui/layouts.py b/ingest_pipeline/cli/tui/layouts.py index ae0c817..4781479 100644 --- a/ingest_pipeline/cli/tui/layouts.py +++ b/ingest_pipeline/cli/tui/layouts.py @@ -163,12 +163,12 @@ class CollapsibleSidebar(Container): else: _ = self.remove_class("collapsed") - def expand(self) -> None: + def expand_sidebar(self) -> None: """Expand sidebar.""" if self.collapsed: self.toggle() - def collapse(self) -> None: + def collapse_sidebar(self) -> None: """Collapse sidebar.""" if not self.collapsed: self.toggle() diff --git a/ingest_pipeline/cli/tui/screens/__pycache__/dashboard.cpython-312.pyc b/ingest_pipeline/cli/tui/screens/__pycache__/dashboard.cpython-312.pyc index 83cf24d..086f416 100644 Binary files a/ingest_pipeline/cli/tui/screens/__pycache__/dashboard.cpython-312.pyc and b/ingest_pipeline/cli/tui/screens/__pycache__/dashboard.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/screens/__pycache__/dialogs.cpython-312.pyc b/ingest_pipeline/cli/tui/screens/__pycache__/dialogs.cpython-312.pyc index aa93382..6b9b049 100644 Binary files a/ingest_pipeline/cli/tui/screens/__pycache__/dialogs.cpython-312.pyc and b/ingest_pipeline/cli/tui/screens/__pycache__/dialogs.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/screens/__pycache__/documents.cpython-312.pyc b/ingest_pipeline/cli/tui/screens/__pycache__/documents.cpython-312.pyc index 142b8dd..9355284 100644 Binary files a/ingest_pipeline/cli/tui/screens/__pycache__/documents.cpython-312.pyc and b/ingest_pipeline/cli/tui/screens/__pycache__/documents.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/screens/__pycache__/ingestion.cpython-312.pyc b/ingest_pipeline/cli/tui/screens/__pycache__/ingestion.cpython-312.pyc index 118c5ce..a92317a 100644 Binary files a/ingest_pipeline/cli/tui/screens/__pycache__/ingestion.cpython-312.pyc and b/ingest_pipeline/cli/tui/screens/__pycache__/ingestion.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/screens/__pycache__/search.cpython-312.pyc b/ingest_pipeline/cli/tui/screens/__pycache__/search.cpython-312.pyc index 0c6efbc..3ca3a76 100644 Binary files a/ingest_pipeline/cli/tui/screens/__pycache__/search.cpython-312.pyc and b/ingest_pipeline/cli/tui/screens/__pycache__/search.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/screens/base.py b/ingest_pipeline/cli/tui/screens/base.py index 04ddc5f..b6c34df 100644 --- a/ingest_pipeline/cli/tui/screens/base.py +++ b/ingest_pipeline/cli/tui/screens/base.py @@ -2,14 +2,14 @@ from __future__ import annotations -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Generic, TypeVar +from typing import TYPE_CHECKING, Generic, TypeVar from textual import work from textual.app import ComposeResult from textual.binding import Binding from textual.containers import Container from textual.screen import ModalScreen, Screen +from textual.widget import Widget from textual.widgets import Button, DataTable, LoadingIndicator, Static from typing_extensions import override @@ -19,16 +19,25 @@ if TYPE_CHECKING: T = TypeVar("T") -class BaseScreen(Screen, ABC): +class BaseScreen(Screen[object]): """Base screen with common functionality.""" - def __init__(self, storage_manager: StorageManager, **kwargs: Any) -> None: + def __init__( + self, + storage_manager: StorageManager, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize base screen.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes) + # Ignore any additional kwargs to avoid type issues self.storage_manager = storage_manager -class CRUDScreen(BaseScreen, Generic[T], ABC): +class CRUDScreen(BaseScreen, Generic[T]): """Base class for Create/Read/Update/Delete operations.""" BINDINGS = [ @@ -39,9 +48,17 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): Binding("escape", "app.pop_screen", "Back"), ] - def __init__(self, storage_manager: StorageManager, **kwargs: Any) -> None: + def __init__( + self, + storage_manager: StorageManager, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize CRUD screen.""" - super().__init__(storage_manager, **kwargs) + super().__init__(storage_manager, name=name, id=id, classes=classes) self.items: list[T] = [] self.selected_item: T | None = None self.loading = False @@ -77,35 +94,29 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): table.add_columns(*self.get_table_columns()) return table - @abstractmethod def get_table_columns(self) -> list[str]: """Get table column headers.""" - pass + raise NotImplementedError("Subclasses must implement get_table_columns") - @abstractmethod async def load_items(self) -> list[T]: """Load items from storage.""" - pass + raise NotImplementedError("Subclasses must implement load_items") - @abstractmethod def item_to_row(self, item: T) -> list[str]: """Convert item to table row.""" - pass + raise NotImplementedError("Subclasses must implement item_to_row") - @abstractmethod async def create_item_dialog(self) -> T | None: """Show create item dialog.""" - pass + raise NotImplementedError("Subclasses must implement create_item_dialog") - @abstractmethod async def edit_item_dialog(self, item: T) -> T | None: """Show edit item dialog.""" - pass + raise NotImplementedError("Subclasses must implement edit_item_dialog") - @abstractmethod async def delete_item(self, item: T) -> bool: """Delete item.""" - pass + raise NotImplementedError("Subclasses must implement delete_item") def on_mount(self) -> None: """Initialize screen.""" @@ -176,17 +187,21 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): self.refresh_items() -class ListScreen(BaseScreen, Generic[T], ABC): +class ListScreen(BaseScreen, Generic[T]): """Base for paginated list views.""" def __init__( self, storage_manager: StorageManager, page_size: int = 20, - **kwargs: Any, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object, ) -> None: """Initialize list screen.""" - super().__init__(storage_manager, **kwargs) + super().__init__(storage_manager, name=name, id=id, classes=classes) self.page_size = page_size self.current_page = 0 self.total_items = 0 @@ -204,25 +219,21 @@ class ListScreen(BaseScreen, Generic[T], ABC): classes="list-container", ) - @abstractmethod def get_title(self) -> str: """Get screen title.""" - pass + raise NotImplementedError("Subclasses must implement get_title") - @abstractmethod def create_filters(self) -> Container: """Create filter widgets.""" - pass + raise NotImplementedError("Subclasses must implement create_filters") - @abstractmethod - def create_list_view(self) -> Any: + def create_list_view(self) -> Widget: """Create list view widget.""" - pass + raise NotImplementedError("Subclasses must implement create_list_view") - @abstractmethod async def load_page(self, page: int, page_size: int) -> tuple[list[T], int]: """Load page of items.""" - pass + raise NotImplementedError("Subclasses must implement load_page") def create_pagination(self) -> Container: """Create pagination controls.""" @@ -249,10 +260,9 @@ class ListScreen(BaseScreen, Generic[T], ABC): finally: self.set_loading(False) - @abstractmethod async def update_list_view(self) -> None: """Update list view with current items.""" - pass + raise NotImplementedError("Subclasses must implement update_list_view") def update_pagination_info(self) -> None: """Update pagination information.""" @@ -285,7 +295,7 @@ class ListScreen(BaseScreen, Generic[T], ABC): self.load_current_page() -class FormScreen(ModalScreen[T], Generic[T], ABC): +class FormScreen(ModalScreen[T], Generic[T]): """Base for input forms with validation.""" BINDINGS = [ @@ -294,9 +304,18 @@ class FormScreen(ModalScreen[T], Generic[T], ABC): Binding("enter", "save", "Save"), ] - def __init__(self, item: T | None = None, **kwargs: Any) -> None: + def __init__( + self, + item: T | None = None, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize form screen.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes) + # Ignore any additional kwargs to avoid type issues self.item = item self.is_edit_mode = item is not None @@ -315,35 +334,30 @@ class FormScreen(ModalScreen[T], Generic[T], ABC): classes="form-container", ) - @abstractmethod def get_item_type(self) -> str: """Get item type name for title.""" - pass + raise NotImplementedError("Subclasses must implement get_item_type") - @abstractmethod def create_form_fields(self) -> Container: """Create form input fields.""" - pass + raise NotImplementedError("Subclasses must implement create_form_fields") - @abstractmethod def validate_form(self) -> tuple[bool, list[str]]: """Validate form data.""" - pass + raise NotImplementedError("Subclasses must implement validate_form") - @abstractmethod def get_form_data(self) -> T: """Get item from form data.""" - pass + raise NotImplementedError("Subclasses must implement get_form_data") def on_mount(self) -> None: """Initialize form.""" if self.is_edit_mode and self.item: self.populate_form(self.item) - @abstractmethod def populate_form(self, item: T) -> None: """Populate form with item data.""" - pass + raise NotImplementedError("Subclasses must implement populate_form") def action_save(self) -> None: """Save form data.""" diff --git a/ingest_pipeline/cli/tui/screens/dashboard.py b/ingest_pipeline/cli/tui/screens/dashboard.py index 3c8e6d4..69de5f1 100644 --- a/ingest_pipeline/cli/tui/screens/dashboard.py +++ b/ingest_pipeline/cli/tui/screens/dashboard.py @@ -212,8 +212,9 @@ class CollectionOverviewScreen(Screen[None]): """Update the metrics cards display.""" try: dashboard_tab = self.query_one("#dashboard") - metrics_cards = dashboard_tab.query(MetricsCard) - if len(metrics_cards) >= 4: + metrics_cards_query = dashboard_tab.query(MetricsCard) + if len(metrics_cards_query) >= 4: + metrics_cards = list(metrics_cards_query) self._update_card_values(metrics_cards) self._update_status_card(metrics_cards[3]) except NoMatches: @@ -221,13 +222,13 @@ class CollectionOverviewScreen(Screen[None]): except Exception as exc: LOGGER.exception("Failed to update dashboard metrics", exc_info=exc) - def _update_card_values(self, metrics_cards: list) -> None: + def _update_card_values(self, metrics_cards: list[MetricsCard]) -> None: """Update individual metric card values.""" metrics_cards[0].query_one(".metrics-value", Static).update(f"{self.total_collections:,}") metrics_cards[1].query_one(".metrics-value", Static).update(f"{self.total_documents:,}") metrics_cards[2].query_one(".metrics-value", Static).update(str(self.active_backends)) - def _update_status_card(self, status_card: object) -> None: + def _update_status_card(self, status_card: MetricsCard) -> None: """Update the system status card.""" if self.active_backends > 0 and self.total_collections > 0: status_text, status_class = "🟢 Healthy", "status-active" @@ -362,8 +363,10 @@ class CollectionOverviewScreen(Screen[None]): collections: list[CollectionInfo] = [] for item in overview: - count_val = int(item.get("count", 0)) - size_mb_val = float(item.get("size_mb", 0.0)) + count_raw = item.get("count", 0) + count_val = int(count_raw) if isinstance(count_raw, (int, str)) else 0 + size_mb_raw = item.get("size_mb", 0.0) + size_mb_val = float(size_mb_raw) if isinstance(size_mb_raw, (int, float, str)) else 0.0 collections.append( CollectionInfo( name=str(item.get("name", "Unknown")), @@ -393,8 +396,10 @@ class CollectionOverviewScreen(Screen[None]): collections: list[CollectionInfo] = [] for item in overview: - count_val = int(item.get("count", 0)) - size_mb_val = float(item.get("size_mb", 0.0)) + count_raw = item.get("count", 0) + count_val = int(count_raw) if isinstance(count_raw, (int, str)) else 0 + size_mb_raw = item.get("size_mb", 0.0) + size_mb_val = float(size_mb_raw) if isinstance(size_mb_raw, (int, float, str)) else 0.0 collection_name = str(item.get("name", "Unknown")) collections.append( CollectionInfo( @@ -504,9 +509,7 @@ class CollectionOverviewScreen(Screen[None]): def action_manage(self) -> None: """Manage documents in selected collection.""" if selected := self.get_selected_collection(): - # Get the appropriate storage backend for the collection - storage_backend = self._get_storage_for_collection(selected) - if storage_backend: + if storage_backend := self._get_storage_for_collection(selected): from .documents import DocumentManagementScreen self.app.push_screen(DocumentManagementScreen(selected, storage_backend)) diff --git a/ingest_pipeline/cli/tui/screens/dialogs.py b/ingest_pipeline/cli/tui/screens/dialogs.py index 86caea7..59ccf17 100644 --- a/ingest_pipeline/cli/tui/screens/dialogs.py +++ b/ingest_pipeline/cli/tui/screens/dialogs.py @@ -1,10 +1,10 @@ """Dialog screens for confirmations and user interactions.""" from pathlib import Path -from typing import TYPE_CHECKING, ClassVar +from typing import TYPE_CHECKING from textual.app import ComposeResult -from textual.binding import Binding, BindingType +from textual.binding import Binding from textual.containers import Container, Horizontal from textual.screen import ModalScreen, Screen from textual.widgets import Button, Footer, Header, LoadingIndicator, RichLog, Static @@ -23,7 +23,7 @@ class ConfirmDeleteScreen(Screen[None]): collection: CollectionInfo parent_screen: "CollectionOverviewScreen" - BINDINGS: ClassVar[list[BindingType]] = [ + BINDINGS = [ Binding("escape", "app.pop_screen", "Cancel"), Binding("y", "confirm_delete", "Yes"), Binding("n", "app.pop_screen", "No"), @@ -145,7 +145,7 @@ class ConfirmDocumentDeleteScreen(Screen[None]): collection: CollectionInfo parent_screen: "DocumentManagementScreen" - BINDINGS: ClassVar[list[BindingType]] = [ + BINDINGS = [ Binding("escape", "app.pop_screen", "Cancel"), Binding("y", "confirm_delete", "Yes"), Binding("n", "app.pop_screen", "No"), @@ -205,9 +205,12 @@ class ConfirmDocumentDeleteScreen(Screen[None]): loading.display = True try: - if self.parent_screen.weaviate: - # Delete documents - results = await self.parent_screen.weaviate.delete_documents( + if hasattr(self.parent_screen, 'storage') and self.parent_screen.storage: + # Delete documents via storage + # The storage should have delete_documents method for weaviate + storage = self.parent_screen.storage + if hasattr(storage, 'delete_documents'): + results = await storage.delete_documents( self.doc_ids, collection_name=self.collection["name"], ) @@ -238,7 +241,7 @@ class LogViewerScreen(ModalScreen[None]): _log_widget: RichLog | None _log_file: Path | None - BINDINGS: ClassVar[list[BindingType]] = [ + BINDINGS = [ Binding("escape", "close", "Close"), Binding("ctrl+l", "close", "Close"), Binding("s", "show_path", "Log File"), @@ -264,21 +267,21 @@ class LogViewerScreen(ModalScreen[None]): def on_mount(self) -> None: """Attach this viewer to the parent application once mounted.""" self._log_widget = self.query_one(RichLog) - from ..app import CollectionManagementApp - if isinstance(self.app, CollectionManagementApp): + if hasattr(self.app, 'attach_log_viewer'): self.app.attach_log_viewer(self) def on_unmount(self) -> None: """Detach from the parent application when closed.""" - from ..app import CollectionManagementApp - if isinstance(self.app, CollectionManagementApp): + if hasattr(self.app, 'detach_log_viewer'): self.app.detach_log_viewer(self) def _get_log_widget(self) -> RichLog: if self._log_widget is None: self._log_widget = self.query_one(RichLog) + if self._log_widget is None: + raise RuntimeError("RichLog widget not found") return self._log_widget def replace_logs(self, lines: list[str]) -> None: diff --git a/ingest_pipeline/cli/tui/screens/documents.py b/ingest_pipeline/cli/tui/screens/documents.py index 09d829e..989a710 100644 --- a/ingest_pipeline/cli/tui/screens/documents.py +++ b/ingest_pipeline/cli/tui/screens/documents.py @@ -10,7 +10,6 @@ from textual.widgets import Button, Footer, Header, Label, LoadingIndicator, Sta from typing_extensions import override from ....storage.base import BaseStorage -from ....storage.weaviate import WeaviateStorage from ..models import CollectionInfo, DocumentInfo from ..widgets import EnhancedDataTable @@ -115,9 +114,9 @@ class DocumentManagementScreen(Screen[None]): description=str(doc.get("description", "")), content_type=str(doc.get("content_type", "text/plain")), content_preview=str(doc.get("content_preview", "")), - word_count=int(doc.get("word_count", 0)) - if str(doc.get("word_count", 0)).isdigit() - else 0, + word_count=( + lambda wc_val: int(wc_val) if isinstance(wc_val, (int, str)) and str(wc_val).isdigit() else 0 + )(doc.get("word_count", 0)), timestamp=str(doc.get("timestamp", "")), ) for i, doc in enumerate(raw_docs) diff --git a/ingest_pipeline/cli/tui/screens/ingestion.py b/ingest_pipeline/cli/tui/screens/ingestion.py index a69d3e9..5608228 100644 --- a/ingest_pipeline/cli/tui/screens/ingestion.py +++ b/ingest_pipeline/cli/tui/screens/ingestion.py @@ -383,12 +383,12 @@ class IngestionScreen(ModalScreen[None]): # Run the Prefect flow for this backend using asyncio.run with timeout import asyncio - async def run_flow_with_timeout() -> IngestionResult: + async def run_flow_with_timeout(current_backend: StorageBackend = backend) -> IngestionResult: return await asyncio.wait_for( create_ingestion_flow( source_url=source_url, source_type=self.selected_type, - storage_backend=backend, + storage_backend=current_backend, collection_name=final_collection_name, progress_callback=progress_reporter, ), @@ -403,7 +403,7 @@ class IngestionScreen(ModalScreen[None]): if result.error_messages: flow_errors.extend([f"{backend.value}: {err}" for err in result.error_messages]) - except asyncio.TimeoutError: + except TimeoutError: error_msg = f"{backend.value}: Timeout after 10 minutes" flow_errors.append(error_msg) progress_reporter(0, f"❌ {backend.value} timed out") diff --git a/ingest_pipeline/cli/tui/screens/search.py b/ingest_pipeline/cli/tui/screens/search.py index e272f89..3b8427f 100644 --- a/ingest_pipeline/cli/tui/screens/search.py +++ b/ingest_pipeline/cli/tui/screens/search.py @@ -112,7 +112,7 @@ class SearchScreen(Screen[None]): async def search_collection(self, query: str) -> None: """Search the collection.""" - loading = self.query_one("#loading") + loading = self.query_one("#loading", LoadingIndicator) table = self.query_one("#results_table", EnhancedDataTable) status = self.query_one("#search_status", Static) @@ -127,7 +127,7 @@ class SearchScreen(Screen[None]): finally: loading.display = False - def _setup_search_ui(self, loading, table, status, query: str) -> None: + def _setup_search_ui(self, loading: LoadingIndicator, table: EnhancedDataTable, status: Static, query: str) -> None: """Setup the search UI elements.""" loading.display = True status.update(f"🔍 Searching for '{query}'...") @@ -142,7 +142,7 @@ class SearchScreen(Screen[None]): return await self.search_openwebui(query) return [] - def _populate_results_table(self, table, results: list[dict[str, str | float]]) -> None: + def _populate_results_table(self, table: EnhancedDataTable, results: list[dict[str, str | float]]) -> None: """Populate the results table with search results.""" for result in results: row_data = self._format_result_row(result) @@ -183,7 +183,7 @@ class SearchScreen(Screen[None]): content = str(content) if content is not None else "" return f"{content[:60]}..." if len(content) > 60 else content - def _format_score(self, score) -> str: + def _format_score(self, score: object) -> str: """Format search score for display.""" if isinstance(score, (int, float)): return f"{score:.3f}" @@ -193,7 +193,7 @@ class SearchScreen(Screen[None]): return str(score) def _update_search_status( - self, status, query: str, results: list[dict[str, str | float]], table + self, status: Static, query: str, results: list[dict[str, str | float]], table: EnhancedDataTable ) -> None: """Update search status and notifications based on results.""" if not results: diff --git a/ingest_pipeline/cli/tui/styles.py b/ingest_pipeline/cli/tui/styles.py index e2d4d42..1c7398f 100644 --- a/ingest_pipeline/cli/tui/styles.py +++ b/ingest_pipeline/cli/tui/styles.py @@ -1106,7 +1106,7 @@ def get_css_for_theme(theme_type: ThemeType) -> str: return css -def apply_theme_to_app(app, theme_type: ThemeType) -> None: +def apply_theme_to_app(app: object, theme_type: ThemeType) -> None: """Apply a theme to a Textual app instance.""" try: css = set_theme(theme_type) @@ -1114,8 +1114,8 @@ def apply_theme_to_app(app, theme_type: ThemeType) -> None: app.stylesheet.clear() app.stylesheet.parse(css) elif hasattr(app, "CSS"): - app.CSS = css - else: + setattr(app, "CSS", css) + elif hasattr(app, "refresh"): # Fallback: try to refresh the app with new CSS app.refresh() except Exception as e: @@ -1127,7 +1127,7 @@ def apply_theme_to_app(app, theme_type: ThemeType) -> None: class ThemeSwitcher: """Helper class for managing theme switching in TUI applications.""" - def __init__(self, app=None): + def __init__(self, app: object | None = None) -> None: self.app = app self.theme_history = [ThemeType.DARK] diff --git a/ingest_pipeline/cli/tui/utils/__pycache__/runners.cpython-312.pyc b/ingest_pipeline/cli/tui/utils/__pycache__/runners.cpython-312.pyc index c216332..91f9b81 100644 Binary files a/ingest_pipeline/cli/tui/utils/__pycache__/runners.cpython-312.pyc and b/ingest_pipeline/cli/tui/utils/__pycache__/runners.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/utils/__pycache__/storage_manager.cpython-312.pyc b/ingest_pipeline/cli/tui/utils/__pycache__/storage_manager.cpython-312.pyc index 02f834f..eeb7c6f 100644 Binary files a/ingest_pipeline/cli/tui/utils/__pycache__/storage_manager.cpython-312.pyc and b/ingest_pipeline/cli/tui/utils/__pycache__/storage_manager.cpython-312.pyc differ diff --git a/ingest_pipeline/cli/tui/utils/runners.py b/ingest_pipeline/cli/tui/utils/runners.py index 033a15c..63d9400 100644 --- a/ingest_pipeline/cli/tui/utils/runners.py +++ b/ingest_pipeline/cli/tui/utils/runners.py @@ -8,12 +8,10 @@ from logging import Logger from logging.handlers import QueueHandler, RotatingFileHandler from pathlib import Path from queue import Queue -from typing import NamedTuple, cast +from typing import NamedTuple from ....config import configure_prefect, get_settings from ....core.models import StorageBackend -from ....storage.openwebui import OpenWebUIStorage -from ....storage.weaviate import WeaviateStorage from .storage_manager import StorageManager @@ -113,11 +111,16 @@ async def run_textual_tui() -> None: ) # Get individual storage instances for backward compatibility - weaviate = cast(WeaviateStorage | None, storage_manager.get_backend(StorageBackend.WEAVIATE)) - openwebui = cast( - OpenWebUIStorage | None, storage_manager.get_backend(StorageBackend.OPEN_WEBUI) - ) - r2r = storage_manager.get_backend(StorageBackend.R2R) + from ....storage.openwebui import OpenWebUIStorage + from ....storage.weaviate import WeaviateStorage + + weaviate_backend = storage_manager.get_backend(StorageBackend.WEAVIATE) + openwebui_backend = storage_manager.get_backend(StorageBackend.OPEN_WEBUI) + r2r_backend = storage_manager.get_backend(StorageBackend.R2R) + + # Type-safe casting to specific storage types + weaviate = weaviate_backend if isinstance(weaviate_backend, WeaviateStorage) else None + openwebui = openwebui_backend if isinstance(openwebui_backend, OpenWebUIStorage) else None # Import here to avoid circular import from ..app import CollectionManagementApp @@ -125,7 +128,7 @@ async def run_textual_tui() -> None: storage_manager, weaviate, openwebui, - r2r, + r2r_backend, log_queue=logging_context.queue, log_formatter=logging_context.formatter, log_file=logging_context.log_file, diff --git a/ingest_pipeline/cli/tui/utils/storage_manager.py b/ingest_pipeline/cli/tui/utils/storage_manager.py index 5188502..28ece27 100644 --- a/ingest_pipeline/cli/tui/utils/storage_manager.py +++ b/ingest_pipeline/cli/tui/utils/storage_manager.py @@ -11,12 +11,13 @@ from ....core.exceptions import StorageError from ....core.models import Document, StorageBackend, StorageConfig from ..models import CollectionInfo, StorageCapabilities +from ....storage.base import BaseStorage +from ....storage.openwebui import OpenWebUIStorage +from ....storage.r2r.storage import R2RStorage +from ....storage.weaviate import WeaviateStorage + if TYPE_CHECKING: from ....config.settings import Settings -from ....storage.weaviate import WeaviateStorage -from ....storage.r2r.storage import R2RStorage -from ....storage.openwebui import OpenWebUIStorage -from ....storage.base import BaseStorage class StorageBackendProtocol(Protocol): diff --git a/ingest_pipeline/cli/tui/widgets/firecrawl_config.py b/ingest_pipeline/cli/tui/widgets/firecrawl_config.py index 4ae2aba..a031915 100644 --- a/ingest_pipeline/cli/tui/widgets/firecrawl_config.py +++ b/ingest_pipeline/cli/tui/widgets/firecrawl_config.py @@ -2,7 +2,8 @@ from __future__ import annotations -from typing import Any, cast +import json +from typing import cast from textual.app import ComposeResult from textual.containers import Container, Horizontal @@ -61,9 +62,17 @@ class ScrapeOptionsForm(Container): } """ - def __init__(self, **kwargs: Any) -> None: + def __init__( + self, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + markup: bool = True, + ) -> None: """Initialize scrape options form.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes, disabled=disabled, markup=markup) @override def compose(self) -> ComposeResult: @@ -136,10 +145,8 @@ class ScrapeOptionsForm(Container): classes="form-section", ) - def get_scrape_options(self) -> dict[str, Any]: + def get_scrape_options(self) -> dict[str, object]: """Get scraping options from form.""" - options: dict[str, Any] = {} - # Collect formats formats = [] if self.query_one("#format_markdown", Checkbox).value: @@ -148,11 +155,12 @@ class ScrapeOptionsForm(Container): formats.append("html") if self.query_one("#format_screenshot", Checkbox).value: formats.append("screenshot") - options["formats"] = formats - - # Content filtering - options["only_main_content"] = self.query_one("#only_main_content", Switch).value - + options: dict[str, object] = { + "formats": formats, + "only_main_content": self.query_one( + "#only_main_content", Switch + ).value, + } include_tags_input = self.query_one("#include_tags", Input).value if include_tags_input.strip(): options["include_tags"] = [tag.strip() for tag in include_tags_input.split(",")] @@ -171,22 +179,26 @@ class ScrapeOptionsForm(Container): return options - def set_scrape_options(self, options: dict[str, Any]) -> None: + def set_scrape_options(self, options: dict[str, object]) -> None: """Set form values from options.""" # Set formats formats = options.get("formats", ["markdown"]) - self.query_one("#format_markdown", Checkbox).value = "markdown" in formats - self.query_one("#format_html", Checkbox).value = "html" in formats - self.query_one("#format_screenshot", Checkbox).value = "screenshot" in formats + formats_list = formats if isinstance(formats, list) else [] + self.query_one("#format_markdown", Checkbox).value = "markdown" in formats_list + self.query_one("#format_html", Checkbox).value = "html" in formats_list + self.query_one("#format_screenshot", Checkbox).value = "screenshot" in formats_list # Set content filtering - self.query_one("#only_main_content", Switch).value = options.get("only_main_content", True) + main_content_val = options.get("only_main_content", True) + self.query_one("#only_main_content", Switch).value = bool(main_content_val) if include_tags := options.get("include_tags", []): - self.query_one("#include_tags", Input).value = ", ".join(include_tags) + include_list = include_tags if isinstance(include_tags, list) else [] + self.query_one("#include_tags", Input).value = ", ".join(str(tag) for tag in include_list) if exclude_tags := options.get("exclude_tags", []): - self.query_one("#exclude_tags", Input).value = ", ".join(exclude_tags) + exclude_list = exclude_tags if isinstance(exclude_tags, list) else [] + self.query_one("#exclude_tags", Input).value = ", ".join(str(tag) for tag in exclude_list) # Set performance wait_for = options.get("wait_for") @@ -231,9 +243,17 @@ class MapOptionsForm(Container): } """ - def __init__(self, **kwargs: Any) -> None: + def __init__( + self, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + markup: bool = True, + ) -> None: """Initialize map options form.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes, disabled=disabled, markup=markup) @override def compose(self) -> ComposeResult: @@ -286,9 +306,9 @@ class MapOptionsForm(Container): classes="form-section", ) - def get_map_options(self) -> dict[str, Any]: + def get_map_options(self) -> dict[str, object]: """Get mapping options from form.""" - options: dict[str, Any] = {} + options: dict[str, object] = {} # Discovery settings search_pattern = self.query_one("#search_pattern", Input).value @@ -314,12 +334,13 @@ class MapOptionsForm(Container): return options - def set_map_options(self, options: dict[str, Any]) -> None: + def set_map_options(self, options: dict[str, object]) -> None: """Set form values from options.""" if search := options.get("search"): self.query_one("#search_pattern", Input).value = str(search) - self.query_one("#include_subdomains", Switch).value = options.get("include_subdomains", False) + subdomains_val = options.get("include_subdomains", False) + self.query_one("#include_subdomains", Switch).value = bool(subdomains_val) # Set limits limit = options.get("limit") @@ -373,9 +394,17 @@ class ExtractOptionsForm(Container): } """ - def __init__(self, **kwargs: Any) -> None: + def __init__( + self, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + markup: bool = True, + ) -> None: """Initialize extract options form.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes, disabled=disabled, markup=markup) @override def compose(self) -> ComposeResult: @@ -429,9 +458,9 @@ class ExtractOptionsForm(Container): classes="form-section", ) - def get_extract_options(self) -> dict[str, Any]: + def get_extract_options(self) -> dict[str, object]: """Get extraction options from form.""" - options: dict[str, Any] = {} + options: dict[str, object] = {} # Extract prompt prompt = self.query_one("#extract_prompt", TextArea).text @@ -442,8 +471,6 @@ class ExtractOptionsForm(Container): schema_text = self.query_one("#extract_schema", TextArea).text if schema_text.strip(): try: - import json - schema = json.loads(schema_text) options["extract_schema"] = schema except json.JSONDecodeError: @@ -452,7 +479,7 @@ class ExtractOptionsForm(Container): return options - def set_extract_options(self, options: dict[str, Any]) -> None: + def set_extract_options(self, options: dict[str, object]) -> None: """Set form values from options.""" if prompt := options.get("extract_prompt"): self.query_one("#extract_prompt", TextArea).text = str(prompt) @@ -551,9 +578,17 @@ class FirecrawlConfigWidget(Container): } """ - def __init__(self, **kwargs: Any) -> None: + def __init__( + self, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + disabled: bool = False, + markup: bool = True, + ) -> None: """Initialize Firecrawl config widget.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes, disabled=disabled, markup=markup) self.current_tab = "scrape" @override @@ -604,7 +639,7 @@ class FirecrawlConfigWidget(Container): def on_button_pressed(self, event: Button.Pressed) -> None: """Handle button presses.""" - if event.button.id.startswith("tab_"): + if event.button.id and event.button.id.startswith("tab_"): tab_name = event.button.id[4:] # Remove "tab_" prefix self.show_tab(tab_name) @@ -622,15 +657,15 @@ class FirecrawlConfigWidget(Container): pass elif self.current_tab == "map": try: - form = self.query_one("#map_form", MapOptionsForm) - map_opts = form.get_map_options() + map_form = self.query_one("#map_form", MapOptionsForm) + map_opts = map_form.get_map_options() options.update(cast(FirecrawlOptions, map_opts)) except Exception: pass elif self.current_tab == "extract": try: - form = self.query_one("#extract_form", ExtractOptionsForm) - extract_opts = form.get_extract_options() + extract_form = self.query_one("#extract_form", ExtractOptionsForm) + extract_opts = extract_form.get_extract_options() options.update(cast(FirecrawlOptions, extract_opts)) except Exception: pass diff --git a/ingest_pipeline/cli/tui/widgets/r2r_widgets.py b/ingest_pipeline/cli/tui/widgets/r2r_widgets.py index c3053c0..80ac429 100644 --- a/ingest_pipeline/cli/tui/widgets/r2r_widgets.py +++ b/ingest_pipeline/cli/tui/widgets/r2r_widgets.py @@ -98,9 +98,11 @@ class ChunkViewer(Container): "id": str(chunk_data.get("id", "")), "document_id": self.document_id, "content": str(chunk_data.get("text", "")), - "start_index": int(chunk_data.get("start_index", 0)), - "end_index": int(chunk_data.get("end_index", 0)), - "metadata": dict(chunk_data.get("metadata", {})), + "start_index": (lambda si: int(si) if isinstance(si, (int, str)) else 0)(chunk_data.get("start_index", 0)), + "end_index": (lambda ei: int(ei) if isinstance(ei, (int, str)) else 0)(chunk_data.get("end_index", 0)), + "metadata": ( + dict(metadata_val) if (metadata_val := chunk_data.get("metadata")) and isinstance(metadata_val, dict) else {} + ), } self.chunks.append(chunk_info) @@ -217,6 +219,8 @@ class EntityGraph(Container): # Parse entities from R2R response entities_list = entities_data.get("entities", []) + if not isinstance(entities_list, list): + entities_list = [] for entity_data in entities_list: entity_info: EntityInfo = { "id": str(entity_data.get("id", "")), @@ -260,7 +264,7 @@ class EntityGraph(Container): tree.root.expand() - def on_tree_node_selected(self, event: Tree.NodeSelected) -> None: + def on_tree_node_selected(self, event: Tree.NodeSelected[EntityInfo]) -> None: """Handle entity selection.""" if hasattr(event.node, "data") and event.node.data: entity = event.node.data @@ -488,7 +492,8 @@ class DocumentOverview(Container): doc_table = self.query_one("#doc_info_table", DataTable) doc_table.add_columns("Property", "Value") - document_info = overview_data.get("document", {}) + document_info_raw = overview_data.get("document", {}) + document_info = document_info_raw if isinstance(document_info_raw, dict) else {} doc_table.add_row("ID", str(document_info.get("id", "N/A"))) doc_table.add_row("Title", str(document_info.get("title", "N/A"))) doc_table.add_row("Created", str(document_info.get("created_at", "N/A"))) diff --git a/ingest_pipeline/config/__init__.py b/ingest_pipeline/config/__init__.py index 914d712..782f56a 100644 --- a/ingest_pipeline/config/__init__.py +++ b/ingest_pipeline/config/__init__.py @@ -4,15 +4,43 @@ from __future__ import annotations from contextlib import ExitStack -from prefect.settings import ( - PREFECT_API_KEY, - PREFECT_API_URL, - PREFECT_DEFAULT_WORK_POOL_NAME, - Setting, - temporary_settings, -) +from prefect.settings import Setting, temporary_settings -from .settings import Settings, get_settings + +# Import Prefect settings with version compatibility - avoid static analysis issues +def _setup_prefect_settings() -> tuple[object, object, object]: + """Setup Prefect settings with proper fallbacks.""" + try: + import prefect.settings as ps + + # Try to get the settings directly + api_key = getattr(ps, "PREFECT_API_KEY", None) + api_url = getattr(ps, "PREFECT_API_URL", None) + work_pool = getattr(ps, "PREFECT_DEFAULT_WORK_POOL_NAME", None) + + if api_key is not None: + return api_key, api_url, work_pool + + # Fallback to registry-based approach + registry = getattr(ps, "PREFECT_SETTING_REGISTRY", None) + if registry is not None: + Setting = getattr(ps, "Setting", None) + if Setting is not None: + api_key = registry.get("PREFECT_API_KEY") or Setting("PREFECT_API_KEY", type_=str, default=None) + api_url = registry.get("PREFECT_API_URL") or Setting("PREFECT_API_URL", type_=str, default=None) + work_pool = registry.get("PREFECT_DEFAULT_WORK_POOL_NAME") or Setting("PREFECT_DEFAULT_WORK_POOL_NAME", type_=str, default=None) + return api_key, api_url, work_pool + + except ImportError: + pass + + # Ultimate fallback + return None, None, None + +PREFECT_API_KEY, PREFECT_API_URL, PREFECT_DEFAULT_WORK_POOL_NAME = _setup_prefect_settings() + +# Import after Prefect settings setup to avoid circular dependencies +from .settings import Settings, get_settings # noqa: E402 __all__ = ["Settings", "get_settings", "configure_prefect"] @@ -25,11 +53,11 @@ def configure_prefect(settings: Settings) -> None: overrides: dict[Setting, str] = {} - if settings.prefect_api_url is not None: + if settings.prefect_api_url is not None and PREFECT_API_URL is not None and isinstance(PREFECT_API_URL, Setting): overrides[PREFECT_API_URL] = str(settings.prefect_api_url) - if settings.prefect_api_key: + if settings.prefect_api_key and PREFECT_API_KEY is not None and isinstance(PREFECT_API_KEY, Setting): overrides[PREFECT_API_KEY] = settings.prefect_api_key - if settings.prefect_work_pool: + if settings.prefect_work_pool and PREFECT_DEFAULT_WORK_POOL_NAME is not None and isinstance(PREFECT_DEFAULT_WORK_POOL_NAME, Setting): overrides[PREFECT_DEFAULT_WORK_POOL_NAME] = settings.prefect_work_pool if not overrides: diff --git a/ingest_pipeline/config/__pycache__/__init__.cpython-312.pyc b/ingest_pipeline/config/__pycache__/__init__.cpython-312.pyc index e061982..0c86130 100644 Binary files a/ingest_pipeline/config/__pycache__/__init__.cpython-312.pyc and b/ingest_pipeline/config/__pycache__/__init__.cpython-312.pyc differ diff --git a/ingest_pipeline/config/__pycache__/settings.cpython-312.pyc b/ingest_pipeline/config/__pycache__/settings.cpython-312.pyc index 61aa137..f7519db 100644 Binary files a/ingest_pipeline/config/__pycache__/settings.cpython-312.pyc and b/ingest_pipeline/config/__pycache__/settings.cpython-312.pyc differ diff --git a/ingest_pipeline/config/settings.py b/ingest_pipeline/config/settings.py index dcb74ee..2d8b95a 100644 --- a/ingest_pipeline/config/settings.py +++ b/ingest_pipeline/config/settings.py @@ -3,6 +3,7 @@ from functools import lru_cache from typing import Annotated, Literal +from prefect.variables import Variable from pydantic import Field, HttpUrl, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -147,3 +148,77 @@ def get_settings() -> Settings: Settings instance """ return Settings() + + +class PrefectVariableConfig: + """Helper class for managing Prefect variables with fallbacks to settings.""" + + def __init__(self) -> None: + self._settings = get_settings() + self._variable_names = [ + "default_batch_size", "max_file_size", "max_crawl_depth", "max_crawl_pages", + "default_storage_backend", "default_collection_prefix", "max_concurrent_tasks", + "request_timeout", "default_schedule_interval" + ] + + def _get_fallback_value(self, name: str, default_value: object = None) -> object: + """Get fallback value from settings or default.""" + return default_value or getattr(self._settings, name, default_value) + + def get_with_fallback(self, name: str, default_value: str | int | float | None = None) -> str | int | float | None: + """Get variable value with fallback synchronously.""" + fallback = self._get_fallback_value(name, default_value) + # Ensure fallback is a type that Variable expects + variable_fallback = str(fallback) if fallback is not None else None + try: + result = Variable.get(name, default=variable_fallback) + # Variable can return various types, convert to our expected types + if isinstance(result, (str, int, float)): + return result + elif result is None: + return None + else: + # Convert other types to string + return str(result) + except Exception: + # Return fallback with proper type + if isinstance(fallback, (str, int, float)) or fallback is None: + return fallback + return str(fallback) if fallback is not None else None + + async def get_with_fallback_async(self, name: str, default_value: str | int | float | None = None) -> str | int | float | None: + """Get variable value with fallback asynchronously.""" + fallback = self._get_fallback_value(name, default_value) + variable_fallback = str(fallback) if fallback is not None else None + try: + result = await Variable.aget(name, default=variable_fallback) + # Variable can return various types, convert to our expected types + if isinstance(result, (str, int, float)): + return result + elif result is None: + return None + else: + # Convert other types to string + return str(result) + except Exception: + # Return fallback with proper type + if isinstance(fallback, (str, int, float)) or fallback is None: + return fallback + return str(fallback) if fallback is not None else None + + def get_ingestion_config(self) -> dict[str, str | int | float | None]: + """Get all ingestion-related configuration variables synchronously.""" + return {name: self.get_with_fallback(name) for name in self._variable_names} + + async def get_ingestion_config_async(self) -> dict[str, str | int | float | None]: + """Get all ingestion-related configuration variables asynchronously.""" + result = {} + for name in self._variable_names: + result[name] = await self.get_with_fallback_async(name) + return result + + +@lru_cache +def get_prefect_config() -> PrefectVariableConfig: + """Get cached Prefect variable configuration helper.""" + return PrefectVariableConfig() diff --git a/ingest_pipeline/core/__pycache__/models.cpython-312.pyc b/ingest_pipeline/core/__pycache__/models.cpython-312.pyc index c0e7f34..719e24c 100644 Binary files a/ingest_pipeline/core/__pycache__/models.cpython-312.pyc and b/ingest_pipeline/core/__pycache__/models.cpython-312.pyc differ diff --git a/ingest_pipeline/core/models.py b/ingest_pipeline/core/models.py index f56ac23..d9ee8c9 100644 --- a/ingest_pipeline/core/models.py +++ b/ingest_pipeline/core/models.py @@ -5,7 +5,8 @@ from enum import Enum from typing import Annotated, TypedDict from uuid import UUID, uuid4 -from pydantic import BaseModel, Field, HttpUrl +from prefect.blocks.core import Block +from pydantic import BaseModel, Field, HttpUrl, SecretStr class IngestionStatus(str, Enum): @@ -44,19 +45,27 @@ class VectorConfig(BaseModel): batch_size: Annotated[int, Field(gt=0, le=1000)] = 100 -class StorageConfig(BaseModel): +class StorageConfig(Block): """Configuration for storage backend.""" + _block_type_name = "Storage Configuration" + _block_type_slug = "storage-config" + _description = "Configures storage backend connections and settings for document ingestion" + backend: StorageBackend endpoint: HttpUrl - api_key: str | None = Field(default=None) + api_key: SecretStr | None = Field(default=None) collection_name: str = Field(default="documents") batch_size: Annotated[int, Field(gt=0, le=1000)] = 100 -class FirecrawlConfig(BaseModel): +class FirecrawlConfig(Block): """Configuration for Firecrawl ingestion (operational parameters only).""" + _block_type_name = "Firecrawl Configuration" + _block_type_slug = "firecrawl-config" + _description = "Configures Firecrawl web scraping and crawling parameters" + formats: list[str] = Field(default_factory=lambda: ["markdown", "html"]) max_depth: Annotated[int, Field(ge=1, le=20)] = 5 limit: Annotated[int, Field(ge=1, le=1000)] = 100 @@ -64,9 +73,13 @@ class FirecrawlConfig(BaseModel): include_subdomains: bool = Field(default=False) -class RepomixConfig(BaseModel): +class RepomixConfig(Block): """Configuration for Repomix ingestion.""" + _block_type_name = "Repomix Configuration" + _block_type_slug = "repomix-config" + _description = "Configures repository ingestion patterns and file processing settings" + include_patterns: list[str] = Field( default_factory=lambda: ["*.py", "*.js", "*.ts", "*.md", "*.yaml", "*.json"] ) @@ -77,9 +90,13 @@ class RepomixConfig(BaseModel): respect_gitignore: bool = Field(default=True) -class R2RConfig(BaseModel): +class R2RConfig(Block): """Configuration for R2R ingestion.""" + _block_type_name = "R2R Configuration" + _block_type_slug = "r2r-config" + _description = "Configures R2R-specific ingestion settings including chunking and graph enrichment" + chunk_size: Annotated[int, Field(ge=100, le=8192)] = 1000 chunk_overlap: Annotated[int, Field(ge=0, le=1000)] = 200 enable_graph_enrichment: bool = Field(default=False) diff --git a/ingest_pipeline/flows/__pycache__/ingestion.cpython-312.pyc b/ingest_pipeline/flows/__pycache__/ingestion.cpython-312.pyc index 6e323f6..5303be4 100644 Binary files a/ingest_pipeline/flows/__pycache__/ingestion.cpython-312.pyc and b/ingest_pipeline/flows/__pycache__/ingestion.cpython-312.pyc differ diff --git a/ingest_pipeline/flows/__pycache__/scheduler.cpython-312.pyc b/ingest_pipeline/flows/__pycache__/scheduler.cpython-312.pyc index 8e63331..8e5b11b 100644 Binary files a/ingest_pipeline/flows/__pycache__/scheduler.cpython-312.pyc and b/ingest_pipeline/flows/__pycache__/scheduler.cpython-312.pyc differ diff --git a/ingest_pipeline/flows/ingestion.py b/ingest_pipeline/flows/ingestion.py index 75554b1..2ad399e 100644 --- a/ingest_pipeline/flows/ingestion.py +++ b/ingest_pipeline/flows/ingestion.py @@ -3,12 +3,13 @@ from __future__ import annotations from collections.abc import Callable -from datetime import UTC, datetime, timedelta -from typing import TYPE_CHECKING, Literal, assert_never, cast +from datetime import UTC, datetime +from typing import TYPE_CHECKING, Literal, TypeAlias, assert_never, cast from prefect import flow, get_run_logger, task -from prefect.cache_policies import NO_CACHE -from prefect.futures import wait +from prefect.blocks.core import Block +from prefect.variables import Variable +from pydantic.types import SecretStr from ..config.settings import Settings from ..core.exceptions import IngestionError @@ -31,8 +32,14 @@ from ..utils.metadata_tagger import MetadataTagger SourceTypeLiteral = Literal["web", "repository", "documentation"] StorageBackendLiteral = Literal["weaviate", "open_webui", "r2r"] -SourceTypeLike = IngestionSource | SourceTypeLiteral -StorageBackendLike = StorageBackend | StorageBackendLiteral +SourceTypeLike: TypeAlias = IngestionSource | SourceTypeLiteral +StorageBackendLike: TypeAlias = StorageBackend | StorageBackendLiteral + + +def _safe_cache_key(prefix: str, params: dict[str, object], key: str) -> str: + """Create a type-safe cache key from task parameters.""" + value = params.get(key, "") + return f"{prefix}_{hash(str(value))}" if TYPE_CHECKING: @@ -65,16 +72,22 @@ async def validate_source_task(source_url: str, source_type: IngestionSource) -> @task(name="initialize_storage", retries=3, retry_delay_seconds=5, tags=["storage"]) -async def initialize_storage_task(config: StorageConfig) -> BaseStorage: +async def initialize_storage_task(config: StorageConfig | str) -> BaseStorage: """ Initialize storage backend. Args: - config: Storage configuration + config: Storage configuration block or block name Returns: Initialized storage adapter """ + # Load block if string provided + if isinstance(config, str): + # Use Block.aload with type slug for better type inference + loaded_block = await Block.aload(f"storage-config/{config}") + config = cast(StorageConfig, loaded_block) + if config.backend == StorageBackend.WEAVIATE: storage = WeaviateStorage(config) elif config.backend == StorageBackend.OPEN_WEBUI: @@ -90,38 +103,48 @@ async def initialize_storage_task(config: StorageConfig) -> BaseStorage: return storage -@task(name="map_firecrawl_site", retries=2, retry_delay_seconds=15, tags=["firecrawl", "map"]) -async def map_firecrawl_site_task(source_url: str, config: FirecrawlConfig) -> list[str]: +@task(name="map_firecrawl_site", retries=2, retry_delay_seconds=15, tags=["firecrawl", "map"], + cache_key_fn=lambda ctx, p: _safe_cache_key("firecrawl_map", p, "source_url")) +async def map_firecrawl_site_task(source_url: str, config: FirecrawlConfig | str) -> list[str]: """Map a site using Firecrawl and return discovered URLs.""" + # Load block if string provided + if isinstance(config, str): + # Use Block.aload with type slug for better type inference + loaded_block = await Block.aload(f"firecrawl-config/{config}") + config = cast(FirecrawlConfig, loaded_block) + ingestor = FirecrawlIngestor(config) mapped = await ingestor.map_site(source_url) return mapped or [source_url] -@task(name="filter_existing_documents", retries=1, retry_delay_seconds=5, tags=["r2r", "dedup"], cache_policy=NO_CACHE) +@task(name="filter_existing_documents", retries=1, retry_delay_seconds=5, tags=["dedup"], + cache_key_fn=lambda ctx, p: _safe_cache_key("filter_docs", p, "urls")) # Cache based on URL list async def filter_existing_documents_task( urls: list[str], - storage_client: R2RStorageType, + storage_client: BaseStorage, stale_after_days: int = 30, + *, + collection_name: str | None = None, ) -> list[str]: - """Filter URLs whose documents are missing or stale in R2R.""" + """Filter URLs to only those that need scraping (missing or stale in storage).""" logger = get_run_logger() - cutoff = datetime.now(UTC) - timedelta(days=stale_after_days) eligible: list[str] = [] for url in urls: document_id = str(FirecrawlIngestor.compute_document_id(url)) - existing: Document | None = await storage_client.retrieve(document_id) - if existing is None: - eligible.append(url) - continue + exists = await storage_client.check_exists( + document_id, + collection_name=collection_name, + stale_after_days=stale_after_days + ) - timestamp = existing.metadata["timestamp"] - if timestamp < cutoff: + if not exists: eligible.append(url) - if skipped := len(urls) - len(eligible): - logger.info("Skipping %s up-to-date pages", skipped) + skipped = len(urls) - len(eligible) + if skipped > 0: + logger.info("Skipping %s up-to-date documents in %s", skipped, storage_client.display_name) return eligible @@ -134,7 +157,8 @@ async def scrape_firecrawl_batch_task( ) -> list[FirecrawlPage]: """Scrape a batch of URLs via Firecrawl.""" ingestor = FirecrawlIngestor(config) - return await ingestor.scrape_pages(batch_urls) + result: list[FirecrawlPage] = await ingestor.scrape_pages(batch_urls) + return result @task(name="annotate_firecrawl_metadata", retries=1, retry_delay_seconds=10, tags=["metadata"]) @@ -153,7 +177,8 @@ async def annotate_firecrawl_metadata_task( settings = get_settings() async with MetadataTagger(llm_endpoint=str(settings.llm_endpoint)) as tagger: - return await tagger.tag_batch(documents) + tagged_documents: list[Document] = await tagger.tag_batch(documents) + return tagged_documents except IngestionError as exc: # pragma: no cover - logging side effect logger = get_run_logger() logger.warning("Metadata tagging failed: %s", exc) @@ -164,7 +189,7 @@ async def annotate_firecrawl_metadata_task( return documents -@task(name="upsert_r2r_documents", retries=2, retry_delay_seconds=20, tags=["storage", "r2r"], cache_policy=NO_CACHE) +@task(name="upsert_r2r_documents", retries=2, retry_delay_seconds=20, tags=["storage", "r2r"]) async def upsert_r2r_documents_task( storage_client: R2RStorageType, documents: list[Document], @@ -187,12 +212,14 @@ async def upsert_r2r_documents_task( return processed, failed -@task(name="ingest_documents", retries=2, retry_delay_seconds=30, tags=["ingestion"], cache_policy=NO_CACHE) +@task(name="ingest_documents", retries=2, retry_delay_seconds=30, tags=["ingestion"]) async def ingest_documents_task( job: IngestionJob, collection_name: str | None = None, - batch_size: int = 50, + batch_size: int | None = None, storage_client: BaseStorage | None = None, + storage_block_name: str | None = None, + ingestor_config_block_name: str | None = None, progress_callback: Callable[[int, str], None] | None = None, ) -> tuple[int, int]: """ @@ -201,8 +228,10 @@ async def ingest_documents_task( Args: job: Ingestion job configuration collection_name: Target collection name - batch_size: Number of documents per batch + batch_size: Number of documents per batch (uses Variable if None) storage_client: Optional pre-initialized storage client + storage_block_name: Optional storage block name to load + ingestor_config_block_name: Optional ingestor config block name to load progress_callback: Optional callback for progress updates Returns: @@ -211,8 +240,22 @@ async def ingest_documents_task( if progress_callback: progress_callback(35, "Creating ingestor and storage clients...") - ingestor = _create_ingestor(job) - storage = storage_client or await _create_storage(job, collection_name) + # Use Variable for batch size if not provided + if batch_size is None: + try: + batch_size_var = await Variable.aget("default_batch_size", default="50") + # Convert Variable result to int, handling various types + if isinstance(batch_size_var, int): + batch_size = batch_size_var + elif isinstance(batch_size_var, (str, float)): + batch_size = int(float(str(batch_size_var))) + else: + batch_size = 50 + except Exception: + batch_size = 50 + + ingestor = await _create_ingestor(job, ingestor_config_block_name) + storage = storage_client or await _create_storage(job, collection_name, storage_block_name) if progress_callback: progress_callback(40, "Starting document processing...") @@ -220,30 +263,50 @@ async def ingest_documents_task( return await _process_documents(ingestor, storage, job, batch_size, collection_name, progress_callback) -def _create_ingestor(job: IngestionJob) -> BaseIngestor: +async def _create_ingestor(job: IngestionJob, config_block_name: str | None = None) -> BaseIngestor: """Create appropriate ingestor based on job source type.""" if job.source_type == IngestionSource.WEB: - config = FirecrawlConfig() + if config_block_name: + # Use Block.aload with type slug for better type inference + loaded_block = await Block.aload(f"firecrawl-config/{config_block_name}") + config = cast(FirecrawlConfig, loaded_block) + else: + # Fallback to default configuration + config = FirecrawlConfig() return FirecrawlIngestor(config) elif job.source_type == IngestionSource.REPOSITORY: - config = RepomixConfig() + if config_block_name: + # Use Block.aload with type slug for better type inference + loaded_block = await Block.aload(f"repomix-config/{config_block_name}") + config = cast(RepomixConfig, loaded_block) + else: + # Fallback to default configuration + config = RepomixConfig() return RepomixIngestor(config) else: raise ValueError(f"Unsupported source: {job.source_type}") -async def _create_storage(job: IngestionJob, collection_name: str | None) -> BaseStorage: +async def _create_storage(job: IngestionJob, collection_name: str | None, storage_block_name: str | None = None) -> BaseStorage: """Create and initialize storage client.""" if collection_name is None: - collection_name = f"docs_{job.source_type.value}" + # Use variable for default collection prefix + prefix = await Variable.aget("default_collection_prefix", default="docs") + collection_name = f"{prefix}_{job.source_type.value}" - from ..config import get_settings + if storage_block_name: + # Load storage config from block + loaded_block = await Block.aload(f"storage-config/{storage_block_name}") + storage_config = cast(StorageConfig, loaded_block) + # Override collection name if provided + storage_config.collection_name = collection_name + else: + # Fallback to building config from settings + from ..config import get_settings + settings = get_settings() + storage_config = _build_storage_config(job, settings, collection_name) - settings = get_settings() - - storage_config = _build_storage_config(job, settings, collection_name) storage = _instantiate_storage(job.storage_backend, storage_config) - await storage.initialize() return storage @@ -257,16 +320,19 @@ def _build_storage_config( StorageBackend.OPEN_WEBUI: settings.openwebui_endpoint, StorageBackend.R2R: settings.get_storage_endpoint("r2r"), } - storage_api_keys = { + storage_api_keys: dict[StorageBackend, str | None] = { StorageBackend.WEAVIATE: settings.get_api_key("weaviate"), StorageBackend.OPEN_WEBUI: settings.get_api_key("openwebui"), StorageBackend.R2R: None, # R2R is self-hosted, no API key needed } + api_key_raw: str | None = storage_api_keys[job.storage_backend] + api_key: SecretStr | None = SecretStr(api_key_raw) if api_key_raw is not None else None + return StorageConfig( backend=job.storage_backend, endpoint=storage_endpoints[job.storage_backend], - api_key=storage_api_keys[job.storage_backend], + api_key=api_key, collection_name=collection_name, ) @@ -324,7 +390,20 @@ async def _process_documents( if progress_callback: progress_callback(45, "Ingesting documents from source...") - async for document in ingestor.ingest(job): + # Use smart ingestion with deduplication if storage supports it + if hasattr(storage, 'check_exists'): + try: + # Try to use the smart ingestion method + document_generator = ingestor.ingest_with_dedup( + job, storage, collection_name=collection_name + ) + except Exception: + # Fall back to regular ingestion if smart method fails + document_generator = ingestor.ingest(job) + else: + document_generator = ingestor.ingest(job) + + async for document in document_generator: batch.append(document) total_documents += 1 @@ -425,16 +504,35 @@ async def firecrawl_to_r2r_flow( r2r_storage = cast("R2RStorageType", storage_client) if progress_callback: - progress_callback(45, "Discovering pages with Firecrawl...") + progress_callback(45, "Checking for existing content before mapping...") - discovered_urls = await map_firecrawl_site_task(str(job.source_url), firecrawl_config) + # Smart mapping: try single URL first to avoid expensive map operation + base_url = str(job.source_url) + single_url_id = str(FirecrawlIngestor.compute_document_id(base_url)) + base_exists = await r2r_storage.check_exists( + single_url_id, collection_name=resolved_collection, stale_after_days=30 + ) + + if base_exists: + # Check if this is a recent single-page update + logger.info("Base URL %s exists and is fresh, skipping expensive mapping", base_url) + if progress_callback: + progress_callback(100, "Content is up to date, no processing needed") + return 0, 0 + + if progress_callback: + progress_callback(50, "Discovering pages with Firecrawl...") + + discovered_urls = await map_firecrawl_site_task(base_url, firecrawl_config) unique_urls = _deduplicate_urls(discovered_urls) logger.info("Discovered %s unique URLs from Firecrawl map", len(unique_urls)) if progress_callback: - progress_callback(55, f"Found {len(unique_urls)} pages, filtering existing content...") + progress_callback(60, f"Found {len(unique_urls)} pages, filtering existing content...") - eligible_urls = await filter_existing_documents_task(unique_urls, r2r_storage) + eligible_urls = await filter_existing_documents_task( + unique_urls, r2r_storage, collection_name=resolved_collection + ) if not eligible_urls: logger.info("All Firecrawl pages are up to date for %s", job.source_url) @@ -443,7 +541,7 @@ async def firecrawl_to_r2r_flow( return 0, 0 if progress_callback: - progress_callback(65, f"Scraping {len(eligible_urls)} new/updated pages...") + progress_callback(70, f"Scraping {len(eligible_urls)} new/updated pages...") batch_size = min(settings.default_batch_size, firecrawl_config.limit) url_batches = _chunk_urls(eligible_urls, batch_size) @@ -462,7 +560,7 @@ async def firecrawl_to_r2r_flow( scraped_pages.extend(batch_pages) if progress_callback: - progress_callback(75, f"Processing {len(scraped_pages)} scraped pages...") + progress_callback(80, f"Processing {len(scraped_pages)} scraped pages...") documents = await annotate_firecrawl_metadata_task(scraped_pages, job) @@ -471,7 +569,7 @@ async def firecrawl_to_r2r_flow( return 0, len(eligible_urls) if progress_callback: - progress_callback(85, f"Storing {len(documents)} documents in R2R...") + progress_callback(90, f"Storing {len(documents)} documents in R2R...") processed, failed = await upsert_r2r_documents_task(r2r_storage, documents, resolved_collection) @@ -481,7 +579,7 @@ async def firecrawl_to_r2r_flow( @task(name="update_job_status", tags=["tracking"]) -def update_job_status_task( +async def update_job_status_task( job: IngestionJob, status: IngestionStatus, processed: int = 0, @@ -575,7 +673,7 @@ async def create_ingestion_flow( # Update status to in progress if progress_callback: progress_callback(20, "Initializing storage...") - job = update_job_status_task(job, IngestionStatus.IN_PROGRESS) + job = await update_job_status_task(job, IngestionStatus.IN_PROGRESS) # Run ingestion if progress_callback: @@ -601,7 +699,7 @@ async def create_ingestion_flow( else: final_status = IngestionStatus.COMPLETED - job = update_job_status_task(job, final_status, processed=processed, _failed=failed) + job = await update_job_status_task(job, final_status, processed=processed, _failed=failed) print(f"Ingestion completed: {processed} processed, {failed} failed") @@ -610,7 +708,7 @@ async def create_ingestion_flow( error_messages.append(str(e)) # Don't reset counts - keep whatever was processed before the error - job = update_job_status_task( + job = await update_job_status_task( job, IngestionStatus.FAILED, processed=processed, _failed=failed, error=str(e) ) diff --git a/ingest_pipeline/flows/scheduler.py b/ingest_pipeline/flows/scheduler.py index 78b1dc7..a806558 100644 --- a/ingest_pipeline/flows/scheduler.py +++ b/ingest_pipeline/flows/scheduler.py @@ -6,6 +6,7 @@ from typing import Literal, Protocol, cast from prefect import serve from prefect.deployments.runner import RunnerDeployment from prefect.schedules import Cron, Interval +from prefect.variables import Variable from ..core.models import IngestionSource, StorageBackend from .ingestion import SourceTypeLike, StorageBackendLike, create_ingestion_flow @@ -30,11 +31,13 @@ def create_scheduled_deployment( storage_backend: StorageBackendLike = StorageBackend.WEAVIATE, schedule_type: Literal["cron", "interval"] = "interval", cron_expression: str | None = None, - interval_minutes: int = 60, + interval_minutes: int | None = None, tags: list[str] | None = None, + storage_block_name: str | None = None, + ingestor_config_block_name: str | None = None, ) -> RunnerDeployment: """ - Create a scheduled deployment for ingestion. + Create a scheduled deployment for ingestion with block support. Args: name: Deployment name @@ -43,12 +46,28 @@ def create_scheduled_deployment( storage_backend: Storage backend schedule_type: Type of schedule cron_expression: Cron expression if using cron - interval_minutes: Interval in minutes if using interval + interval_minutes: Interval in minutes (uses Variable if None) tags: Optional tags for deployment + storage_block_name: Optional storage block name + ingestor_config_block_name: Optional ingestor config block name Returns: Deployment configuration """ + # Use Variable for interval if not provided + if interval_minutes is None: + try: + interval_var = Variable.get("default_schedule_interval", default="60") + # Convert Variable result to int, handling various types + if isinstance(interval_var, int): + interval_minutes = interval_var + elif isinstance(interval_var, (str, float)): + interval_minutes = int(float(str(interval_var))) + else: + interval_minutes = 60 + except Exception: + interval_minutes = 60 + # Create schedule if schedule_type == "cron" and cron_expression: schedule = Cron(cron_expression, timezone="UTC") @@ -62,18 +81,27 @@ def create_scheduled_deployment( if tags is None: tags = [source_enum.value, backend_enum.value] + # Create deployment parameters with block support + parameters = { + "source_url": source_url, + "source_type": source_enum.value, + "storage_backend": backend_enum.value, + "validate_first": True, + } + + # Add block names if provided + if storage_block_name: + parameters["storage_block_name"] = storage_block_name + if ingestor_config_block_name: + parameters["ingestor_config_block_name"] = ingestor_config_block_name + # Create deployment # The flow decorator adds the to_deployment method at runtime to_deployment = create_ingestion_flow.to_deployment deployment = to_deployment( name=name, schedule=schedule, - parameters={ - "source_url": source_url, - "source_type": source_enum.value, - "storage_backend": backend_enum.value, - "validate_first": True, - }, + parameters=parameters, tags=tags, description=f"Scheduled ingestion from {source_url}", ) diff --git a/ingest_pipeline/ingestors/__pycache__/base.cpython-312.pyc b/ingest_pipeline/ingestors/__pycache__/base.cpython-312.pyc index b217c5c..90833af 100644 Binary files a/ingest_pipeline/ingestors/__pycache__/base.cpython-312.pyc and b/ingest_pipeline/ingestors/__pycache__/base.cpython-312.pyc differ diff --git a/ingest_pipeline/ingestors/__pycache__/firecrawl.cpython-312.pyc b/ingest_pipeline/ingestors/__pycache__/firecrawl.cpython-312.pyc index 926c004..9ff5513 100644 Binary files a/ingest_pipeline/ingestors/__pycache__/firecrawl.cpython-312.pyc and b/ingest_pipeline/ingestors/__pycache__/firecrawl.cpython-312.pyc differ diff --git a/ingest_pipeline/ingestors/__pycache__/repomix.cpython-312.pyc b/ingest_pipeline/ingestors/__pycache__/repomix.cpython-312.pyc index ba3761d..11c2959 100644 Binary files a/ingest_pipeline/ingestors/__pycache__/repomix.cpython-312.pyc and b/ingest_pipeline/ingestors/__pycache__/repomix.cpython-312.pyc differ diff --git a/ingest_pipeline/ingestors/base.py b/ingest_pipeline/ingestors/base.py index 33968fa..d6870de 100644 --- a/ingest_pipeline/ingestors/base.py +++ b/ingest_pipeline/ingestors/base.py @@ -1,10 +1,16 @@ """Base ingestor interface.""" +from __future__ import annotations + from abc import ABC, abstractmethod from collections.abc import AsyncGenerator +from typing import TYPE_CHECKING from ..core.models import Document, IngestionJob +if TYPE_CHECKING: + from ..storage.base import BaseStorage + class BaseIngestor(ABC): """Abstract base class for all ingestors.""" @@ -47,3 +53,30 @@ class BaseIngestor(ABC): Estimated number of documents """ pass # pragma: no cover + + async def ingest_with_dedup( + self, + job: IngestionJob, + storage_client: BaseStorage, + *, + collection_name: str | None = None, + stale_after_days: int = 30, + ) -> AsyncGenerator[Document, None]: + """ + Ingest documents with duplicate detection (optional optimization). + + Default implementation falls back to regular ingestion. + Subclasses can override to provide optimized deduplication. + + Args: + job: The ingestion job configuration + storage_client: Storage client to check for existing documents + collection_name: Collection to check for duplicates + stale_after_days: Consider documents stale after this many days + + Yields: + Documents from the source (with deduplication if implemented) + """ + # Default implementation: fall back to regular ingestion + async for document in self.ingest(job): + yield document diff --git a/ingest_pipeline/ingestors/firecrawl.py b/ingest_pipeline/ingestors/firecrawl.py index 2a775ac..f734f66 100644 --- a/ingest_pipeline/ingestors/firecrawl.py +++ b/ingest_pipeline/ingestors/firecrawl.py @@ -6,6 +6,7 @@ import re from collections.abc import AsyncGenerator, Awaitable, Callable from dataclasses import dataclass from datetime import UTC, datetime +from typing import TYPE_CHECKING from urllib.parse import urlparse from uuid import NAMESPACE_URL, UUID, uuid5 @@ -23,8 +24,11 @@ from ..core.models import ( ) from .base import BaseIngestor +if TYPE_CHECKING: + from ..storage.base import BaseStorage -class FirecrawlError(IngestionError): # type: ignore[misc] + +class FirecrawlError(IngestionError): """Base exception for Firecrawl-related errors.""" def __init__(self, message: str, status_code: int | None = None) -> None: @@ -161,6 +165,55 @@ class FirecrawlIngestor(BaseIngestor): for page in pages: yield self.create_document(page, job) + async def ingest_with_dedup( + self, + job: IngestionJob, + storage_client: "BaseStorage", + *, + collection_name: str | None = None, + stale_after_days: int = 30, + ) -> AsyncGenerator[Document, None]: + """ + Ingest documents with duplicate detection to avoid unnecessary scraping. + + Args: + job: The ingestion job configuration + storage_client: Storage client to check for existing documents + collection_name: Collection to check for duplicates + stale_after_days: Consider documents stale after this many days + + Yields: + Documents from the web source (only new/stale ones) + """ + url = str(job.source_url) + + # First, map the site to understand its structure + site_map = await self.map_site(url) or [url] + + # Filter out URLs that already exist in storage and are fresh + eligible_urls: list[str] = [] + for check_url in site_map: + document_id = str(self.compute_document_id(check_url)) + exists = await storage_client.check_exists( + document_id, + collection_name=collection_name, + stale_after_days=stale_after_days + ) + if not exists: + eligible_urls.append(check_url) + + if not eligible_urls: + return # No new documents to scrape + + # Process eligible pages in batches + batch_size = 10 + for i in range(0, len(eligible_urls), batch_size): + batch_urls = eligible_urls[i : i + batch_size] + pages = await self.scrape_pages(batch_urls) + + for page in pages: + yield self.create_document(page, job) + async def map_site(self, url: str) -> list[str]: """Public wrapper for mapping a site.""" @@ -391,7 +444,7 @@ class FirecrawlIngestor(BaseIngestor): else: avg_sentence_length = words / sentences # Simplified readability score (0-100, higher is more readable) - readability_score = max(0, min(100, 100 - (avg_sentence_length - 15) * 2)) + readability_score = max(0.0, min(100.0, 100.0 - (avg_sentence_length - 15.0) * 2.0)) # Completeness score based on structure completeness_factors = 0 @@ -478,11 +531,15 @@ class FirecrawlIngestor(BaseIngestor): "domain": domain_info["domain"], "site_name": domain_info["site_name"], # Document structure - "heading_hierarchy": structure_info["heading_hierarchy"], - "section_depth": structure_info["section_depth"], - "has_code_blocks": structure_info["has_code_blocks"], - "has_images": structure_info["has_images"], - "has_links": structure_info["has_links"], + "heading_hierarchy": ( + list(hierarchy_val) if (hierarchy_val := structure_info.get("heading_hierarchy")) and isinstance(hierarchy_val, (list, tuple)) else [] + ), + "section_depth": ( + int(depth_val) if (depth_val := structure_info.get("section_depth")) and isinstance(depth_val, (int, str)) else 0 + ), + "has_code_blocks": bool(structure_info.get("has_code_blocks", False)), + "has_images": bool(structure_info.get("has_images", False)), + "has_links": bool(structure_info.get("has_links", False)), # Processing metadata "extraction_method": "firecrawl", "last_modified": datetime.fromisoformat(page.sitemap_last_modified) diff --git a/ingest_pipeline/ingestors/repomix.py b/ingest_pipeline/ingestors/repomix.py index 5e41439..281a952 100644 --- a/ingest_pipeline/ingestors/repomix.py +++ b/ingest_pipeline/ingestors/repomix.py @@ -1,9 +1,9 @@ """Repomix ingestor for Git repositories.""" import asyncio +import re import subprocess import tempfile -import re from collections.abc import AsyncGenerator from datetime import UTC, datetime from pathlib import Path @@ -400,16 +400,13 @@ class RepomixIngestor(BaseIngestor): # Handle different URL formats if 'github.com' in repo_url or 'gitlab.com' in repo_url: - # Extract from URLs like https://github.com/org/repo.git - path_match = re.search(r'/([^/]+)/([^/]+?)(?:\.git)?/?$', repo_url) - if path_match: - org_name = path_match.group(1) - repo_name = path_match.group(2) - else: - # Try to extract from generic git URLs - path_match = re.search(r'/([^/]+?)(?:\.git)?/?$', repo_url) - if path_match: - repo_name = path_match.group(1) + if path_match := re.search( + r'/([^/]+)/([^/]+?)(?:\.git)?/?$', repo_url + ): + org_name = path_match[1] + repo_name = path_match[2] + elif path_match := re.search(r'/([^/]+?)(?:\.git)?/?$', repo_url): + repo_name = path_match[1] return { 'repository_name': repo_name or 'unknown', @@ -485,28 +482,18 @@ class RepomixIngestor(BaseIngestor): # Build rich metadata metadata: DocumentMetadata = { - # Core required fields "source_url": str(job.source_url), "timestamp": datetime.now(UTC), "content_type": content_type, "word_count": len(content.split()), "char_count": len(content), - - # Basic fields - "title": f"{file_path}" + (f" (chunk {chunk_index})" if chunk_index > 0 else ""), + "title": f"{file_path}" + + (f" (chunk {chunk_index})" if chunk_index > 0 else ""), "description": f"Repository file: {file_path}", - - # Content categorization "category": "source_code" if programming_language else "documentation", "language": programming_language or "text", - - # Document structure from code analysis - "has_code_blocks": True if programming_language else False, - - # Processing metadata + "has_code_blocks": bool(programming_language), "extraction_method": "repomix", - - # Repository-specific fields "file_path": file_path, "programming_language": programming_language, } diff --git a/ingest_pipeline/py.typed b/ingest_pipeline/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/ingest_pipeline/storage/__pycache__/base.cpython-312.pyc b/ingest_pipeline/storage/__pycache__/base.cpython-312.pyc index 778b61f..dcc2976 100644 Binary files a/ingest_pipeline/storage/__pycache__/base.cpython-312.pyc and b/ingest_pipeline/storage/__pycache__/base.cpython-312.pyc differ diff --git a/ingest_pipeline/storage/__pycache__/openwebui.cpython-312.pyc b/ingest_pipeline/storage/__pycache__/openwebui.cpython-312.pyc index 7ae9ae8..61862c7 100644 Binary files a/ingest_pipeline/storage/__pycache__/openwebui.cpython-312.pyc and b/ingest_pipeline/storage/__pycache__/openwebui.cpython-312.pyc differ diff --git a/ingest_pipeline/storage/__pycache__/weaviate.cpython-312.pyc b/ingest_pipeline/storage/__pycache__/weaviate.cpython-312.pyc index 4d66e3e..9990893 100644 Binary files a/ingest_pipeline/storage/__pycache__/weaviate.cpython-312.pyc and b/ingest_pipeline/storage/__pycache__/weaviate.cpython-312.pyc differ diff --git a/ingest_pipeline/storage/base.py b/ingest_pipeline/storage/base.py index 35a6617..18b669a 100644 --- a/ingest_pipeline/storage/base.py +++ b/ingest_pipeline/storage/base.py @@ -75,6 +75,40 @@ class BaseStorage(ABC): """ raise NotImplementedError(f"{self.__class__.__name__} doesn't support document retrieval") + async def check_exists( + self, document_id: str, *, collection_name: str | None = None, stale_after_days: int = 30 + ) -> bool: + """ + Check if a document exists and is not stale. + + Args: + document_id: Document ID to check + collection_name: Collection to check in + stale_after_days: Consider document stale after this many days + + Returns: + True if document exists and is not stale, False otherwise + """ + try: + document = await self.retrieve(document_id, collection_name=collection_name) + if document is None: + return False + + # Check staleness if timestamp is available + if "timestamp" in document.metadata: + from datetime import UTC, datetime, timedelta + timestamp_obj = document.metadata["timestamp"] + if isinstance(timestamp_obj, datetime): + timestamp = timestamp_obj + cutoff = datetime.now(UTC) - timedelta(days=stale_after_days) + return timestamp >= cutoff + + # If no timestamp, assume it exists and is valid + return True + except Exception: + # Backend doesn't support retrieval, assume doesn't exist + return False + def search( self, query: str, @@ -130,6 +164,15 @@ class BaseStorage(ABC): """ return [] + async def describe_collections(self) -> list[dict[str, object]]: + """ + Describe available collections with metadata (if supported by backend). + + Returns: + List of collection metadata dictionaries, empty list if not supported + """ + return [] + async def list_documents( self, limit: int = 100, @@ -159,4 +202,5 @@ class BaseStorage(ABC): Default implementation does nothing. """ - pass + # Default implementation - storage backends can override to cleanup connections + return None diff --git a/ingest_pipeline/storage/openwebui.py b/ingest_pipeline/storage/openwebui.py index d61bc3f..6838b85 100644 --- a/ingest_pipeline/storage/openwebui.py +++ b/ingest_pipeline/storage/openwebui.py @@ -274,6 +274,25 @@ class OpenWebUIStorage(BaseStorage): except Exception as e: raise StorageError(f"Failed to store batch: {e}") from e + @override + async def retrieve( + self, document_id: str, *, collection_name: str | None = None + ) -> Document | None: + """ + OpenWebUI doesn't support document retrieval by ID. + + Args: + document_id: File ID (not supported) + collection_name: Collection name (not used) + + Returns: + Always None - retrieval not supported + """ + # OpenWebUI uses file-based storage without direct document retrieval + # This will cause the base check_exists method to return False, + # which means documents will always be re-scraped for OpenWebUI + raise NotImplementedError("OpenWebUI doesn't support document retrieval by ID") + @override async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: """ @@ -414,69 +433,57 @@ class OpenWebUIStorage(BaseStorage): count: int size_mb: float - async def describe_collections(self) -> list[CollectionSummary]: + + async def _get_knowledge_base_count(self, kb: dict[str, object]) -> int: + """Get the file count for a knowledge base.""" + kb_id = kb.get("id") + name = kb.get("name", "Unknown") + + if not kb_id: + return self._count_files_from_basic_info(kb) + + return await self._count_files_from_detailed_info(str(kb_id), str(name), kb) + + def _count_files_from_basic_info(self, kb: dict[str, object]) -> int: + """Count files from basic knowledge base info.""" + files = kb.get("files", []) + return len(files) if isinstance(files, list) and files is not None else 0 + + async def _count_files_from_detailed_info(self, kb_id: str, name: str, kb: dict[str, object]) -> int: + """Count files by fetching detailed knowledge base info.""" + try: + LOGGER.debug(f"Fetching detailed info for KB '{name}' from /api/v1/knowledge/{kb_id}") + detail_response = await self.client.get(f"/api/v1/knowledge/{kb_id}") + detail_response.raise_for_status() + detailed_kb = detail_response.json() + + files = detailed_kb.get("files", []) + count = len(files) if isinstance(files, list) and files is not None else 0 + + LOGGER.info(f"Knowledge base '{name}' (ID: {kb_id}): found {count} files") + return count + + except Exception as e: + LOGGER.warning(f"Failed to get detailed info for KB '{name}' (ID: {kb_id}): {e}") + return self._count_files_from_basic_info(kb) + + async def describe_collections(self) -> list[dict[str, object]]: """Return metadata about each knowledge base.""" try: - # First get the list of knowledge bases - response = await self.client.get("/api/v1/knowledge/") - response.raise_for_status() - knowledge_bases = response.json() + knowledge_bases = await self._fetch_knowledge_bases() + collections: list[dict[str, object]] = [] - LOGGER.info(f"OpenWebUI returned {len(knowledge_bases)} knowledge bases") - LOGGER.debug(f"Knowledge bases structure: {knowledge_bases}") - - collections: list[OpenWebUIStorage.CollectionSummary] = [] for kb in knowledge_bases: if not isinstance(kb, dict): continue - kb_id = kb.get("id") + count = await self._get_knowledge_base_count(kb) name = kb.get("name", "Unknown") - - LOGGER.info(f"Processing knowledge base: '{name}' (ID: {kb_id})") - LOGGER.debug(f"KB structure: {kb}") - - if not kb_id: - # If no ID, fall back to basic count from list response - files = kb.get("files", []) - if files is None: - files = [] - count = len(files) if isinstance(files, list) else 0 - else: - # Get detailed knowledge base information using the correct endpoint - try: - LOGGER.debug(f"Fetching detailed info for KB '{name}' from /api/v1/knowledge/{kb_id}") - detail_response = await self.client.get(f"/api/v1/knowledge/{kb_id}") - detail_response.raise_for_status() - detailed_kb = detail_response.json() - - LOGGER.debug(f"Detailed KB response: {detailed_kb}") - - files = detailed_kb.get("files", []) - if files is None: - files = [] - count = len(files) if isinstance(files, list) else 0 - - # Debug logging - LOGGER.info(f"Knowledge base '{name}' (ID: {kb_id}): found {count} files") - if count > 0 and len(files) > 0: - LOGGER.debug(f"First file structure: {files[0] if files else 'No files'}") - elif count == 0: - LOGGER.warning(f"Knowledge base '{name}' has 0 files. Files field type: {type(files)}, value: {files}") - - except Exception as e: - LOGGER.warning(f"Failed to get detailed info for KB '{name}' (ID: {kb_id}): {e}") - # Fallback to basic files list if detailed fetch fails - files = kb.get("files", []) - if files is None: - files = [] - count = len(files) if isinstance(files, list) else 0 - LOGGER.info(f"Fallback count for KB '{name}': {count}") - size_mb = count * 0.5 # rough heuristic - summary: OpenWebUIStorage.CollectionSummary = { + + summary: dict[str, object] = { "name": str(name), - "count": int(count), + "count": count, "size_mb": float(size_mb), } collections.append(summary) @@ -500,7 +507,10 @@ class OpenWebUIStorage(BaseStorage): # If no collection name provided, return total across all collections try: collections = await self.describe_collections() - return sum(collection["count"] for collection in collections) + return sum( + int(collection["count"]) if isinstance(collection["count"], (int, str)) else 0 + for collection in collections + ) except Exception: return 0 diff --git a/ingest_pipeline/storage/r2r/__pycache__/storage.cpython-312.pyc b/ingest_pipeline/storage/r2r/__pycache__/storage.cpython-312.pyc index b69bcdf..1efdbbb 100644 Binary files a/ingest_pipeline/storage/r2r/__pycache__/storage.cpython-312.pyc and b/ingest_pipeline/storage/r2r/__pycache__/storage.cpython-312.pyc differ diff --git a/ingest_pipeline/storage/r2r/collections.py b/ingest_pipeline/storage/r2r/collections.py index 53bde78..ce1b5c0 100644 --- a/ingest_pipeline/storage/r2r/collections.py +++ b/ingest_pipeline/storage/r2r/collections.py @@ -62,7 +62,8 @@ class R2RCollections: name=name, description=description, ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError(f"Failed to create collection '{name}': {e}") from e @@ -80,7 +81,8 @@ class R2RCollections: """ try: response = await self.client.collections.retrieve(str(collection_id)) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError(f"Failed to retrieve collection {collection_id}: {e}") from e @@ -109,7 +111,8 @@ class R2RCollections: name=name, description=description, ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError(f"Failed to update collection {collection_id}: {e}") from e @@ -153,7 +156,8 @@ class R2RCollections: limit=limit, owner_only=owner_only, ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError(f"Failed to list collections: {e}") from e @@ -202,7 +206,8 @@ class R2RCollections: id=str(collection_id), document_id=str(document_id), ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError( f"Failed to add document {document_id} to collection {collection_id}: {e}" @@ -254,7 +259,8 @@ class R2RCollections: offset=offset, limit=limit, ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError( f"Failed to list documents in collection {collection_id}: {e}" @@ -278,7 +284,8 @@ class R2RCollections: id=str(collection_id), user_id=str(user_id), ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError( f"Failed to add user {user_id} to collection {collection_id}: {e}" @@ -330,7 +337,8 @@ class R2RCollections: offset=offset, limit=limit, ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError(f"Failed to list users for collection {collection_id}: {e}") from e @@ -359,7 +367,8 @@ class R2RCollections: run_with_orchestration=run_with_orchestration, settings=cast(dict[str, object], settings or {}), ) - return cast(JsonData, response.results.model_dump()) + # response.results is a list, not a model with model_dump() + return cast(JsonData, response.results if isinstance(response.results, list) else []) except Exception as e: raise StorageError( f"Failed to extract entities from collection {collection_id}: {e}" diff --git a/ingest_pipeline/storage/r2r/storage.py b/ingest_pipeline/storage/r2r/storage.py index 770f758..4c7e068 100644 --- a/ingest_pipeline/storage/r2r/storage.py +++ b/ingest_pipeline/storage/r2r/storage.py @@ -9,10 +9,15 @@ from datetime import UTC, datetime from typing import Self, TypeVar, cast from uuid import UUID, uuid4 -import httpx -from r2r import R2RAsyncClient, R2RException +from r2r import R2RAsyncClient from typing_extensions import override +# Direct imports for runtime and type checking +# Note: Some type checkers (basedpyright/Pyrefly) may report import issues +# but these work correctly at runtime and with mypy +from httpx import AsyncClient, HTTPStatusError +from r2r import R2RException + from ...core.exceptions import StorageError from ...core.models import Document, DocumentMetadata, IngestionSource, StorageConfig from ..base import BaseStorage @@ -83,7 +88,7 @@ class R2RStorage(BaseStorage): try: # Ensure we have an event loop try: - asyncio.get_running_loop() + _ = asyncio.get_running_loop() except RuntimeError: # No event loop running, this should not happen in async context # but let's be defensive @@ -93,7 +98,7 @@ class R2RStorage(BaseStorage): # Test connection using direct HTTP call to v3 API endpoint = self.endpoint - client = httpx.AsyncClient() + client = AsyncClient() try: response = await client.get(f"{endpoint}/v3/collections") response.raise_for_status() @@ -107,7 +112,7 @@ class R2RStorage(BaseStorage): """Get or create collection by name.""" try: endpoint = self.endpoint - client = httpx.AsyncClient() + client = AsyncClient() try: # List collections and find by name response = await client.get(f"{endpoint}/v3/collections") @@ -203,7 +208,7 @@ class R2RStorage(BaseStorage): for attempt in range(max_retries): try: - async with httpx.AsyncClient() as http_client: + async with AsyncClient() as http_client: # Use files parameter but with string values for multipart/form-data # This matches the cURL -F behavior more closely metadata = self._build_metadata(document) @@ -264,7 +269,7 @@ class R2RStorage(BaseStorage): doc_response = response.json() break # Success - exit retry loop - except httpx.TimeoutException: + except (OSError, asyncio.TimeoutError): if attempt < max_retries - 1: print( f"Timeout for document {requested_id}, retrying in {retry_delay}s..." @@ -274,7 +279,7 @@ class R2RStorage(BaseStorage): continue else: raise - except httpx.HTTPStatusError as e: + except HTTPStatusError as e: if e.response.status_code >= 500 and attempt < max_retries - 1: print( f"Server error {e.response.status_code} for document {requested_id}, retrying in {retry_delay}s..." @@ -470,7 +475,7 @@ class R2RStorage(BaseStorage): elif description := metadata_map.get("description"): metadata["description"] = cast(str | None, description) if tags := metadata_map.get("tags"): - metadata["tags"] = _as_sequence(tags) if isinstance(tags, list) else [] + metadata["tags"] = [str(tag) for tag in tags] if isinstance(tags, list) else [] if category := metadata_map.get("category"): metadata["category"] = str(category) if section := metadata_map.get("section"): @@ -502,13 +507,15 @@ class R2RStorage(BaseStorage): if last_modified := metadata_map.get("last_modified"): metadata["last_modified"] = _as_datetime(last_modified) if readability_score := metadata_map.get("readability_score"): - metadata["readability_score"] = ( - float(readability_score) if readability_score is not None else None - ) + try: + metadata["readability_score"] = float(str(readability_score)) + except (ValueError, TypeError): + metadata["readability_score"] = None if completeness_score := metadata_map.get("completeness_score"): - metadata["completeness_score"] = ( - float(completeness_score) if completeness_score is not None else None - ) + try: + metadata["completeness_score"] = float(str(completeness_score)) + except (ValueError, TypeError): + metadata["completeness_score"] = None source_value = str(metadata_map.get("ingestion_source", IngestionSource.WEB.value)) try: @@ -609,7 +616,7 @@ class R2RStorage(BaseStorage): """Get document count in collection.""" try: endpoint = self.endpoint - client = httpx.AsyncClient() + client = AsyncClient() try: # Get collections and find the count for the specific collection response = await client.get(f"{endpoint}/v3/collections") @@ -677,7 +684,7 @@ class R2RStorage(BaseStorage): """List all available collections.""" try: endpoint = self.endpoint - client = httpx.AsyncClient() + client = AsyncClient() try: response = await client.get(f"{endpoint}/v3/collections") response.raise_for_status() @@ -777,7 +784,7 @@ class R2RStorage(BaseStorage): collection_id = await self._ensure_collection(collection_name) # Use the collections API to list documents in a specific collection endpoint = self.endpoint - client = httpx.AsyncClient() + client = AsyncClient() try: params = {"offset": offset, "limit": limit} response = await client.get( diff --git a/ingest_pipeline/storage/weaviate.py b/ingest_pipeline/storage/weaviate.py index 9b9ce2c..c1ee5a3 100644 --- a/ingest_pipeline/storage/weaviate.py +++ b/ingest_pipeline/storage/weaviate.py @@ -22,7 +22,6 @@ from ..core.models import Document, DocumentMetadata, IngestionSource, StorageCo from ..utils.vectorizer import Vectorizer from .base import BaseStorage - VectorContainer: TypeAlias = Mapping[str, object] | Sequence[object] | None @@ -591,13 +590,13 @@ class WeaviateStorage(BaseStorage): except Exception as e: raise StorageError(f"Failed to list collections: {e}") from e - async def describe_collections(self) -> list[dict[str, str | int | float]]: + async def describe_collections(self) -> list[dict[str, object]]: """Return metadata for each Weaviate collection.""" if not self.client: raise StorageError("Weaviate client not initialized") try: - collections: list[dict[str, str | int | float]] = [] + collections: list[dict[str, object]] = [] for name in self.client.collections.list_all(): collection_obj = self.client.collections.get(name) if not collection_obj: @@ -808,7 +807,7 @@ class WeaviateStorage(BaseStorage): offset: int = 0, *, collection_name: str | None = None, - ) -> list[dict[str, str | int]]: + ) -> list[dict[str, object]]: """ List documents in the collection with pagination. @@ -830,7 +829,7 @@ class WeaviateStorage(BaseStorage): limit=limit, offset=offset, return_metadata=["creation_time"] ) - documents = [] + documents: list[dict[str, object]] = [] for obj in response.objects: props = self._coerce_properties( obj.properties, @@ -849,7 +848,7 @@ class WeaviateStorage(BaseStorage): else: word_count = 0 - doc_info: dict[str, str | int] = { + doc_info: dict[str, object] = { "id": str(obj.uuid), "title": str(props.get("title", "Untitled")), "source_url": str(props.get("source_url", "")), diff --git a/ingest_pipeline/utils/__pycache__/metadata_tagger.cpython-312.pyc b/ingest_pipeline/utils/__pycache__/metadata_tagger.cpython-312.pyc index 64acc1a..81989d6 100644 Binary files a/ingest_pipeline/utils/__pycache__/metadata_tagger.cpython-312.pyc and b/ingest_pipeline/utils/__pycache__/metadata_tagger.cpython-312.pyc differ diff --git a/ingest_pipeline/utils/__pycache__/vectorizer.cpython-312.pyc b/ingest_pipeline/utils/__pycache__/vectorizer.cpython-312.pyc index 5144ae1..cfffc2c 100644 Binary files a/ingest_pipeline/utils/__pycache__/vectorizer.cpython-312.pyc and b/ingest_pipeline/utils/__pycache__/vectorizer.cpython-312.pyc differ diff --git a/ingest_pipeline/utils/metadata_tagger.py b/ingest_pipeline/utils/metadata_tagger.py index 9751af9..1f00b16 100644 --- a/ingest_pipeline/utils/metadata_tagger.py +++ b/ingest_pipeline/utils/metadata_tagger.py @@ -2,7 +2,7 @@ import json from datetime import UTC, datetime -from typing import TypedDict, cast +from typing import Protocol, TypedDict, cast import httpx @@ -10,6 +10,41 @@ from ..core.exceptions import IngestionError from ..core.models import Document +class HttpResponse(Protocol): + """Protocol for HTTP response.""" + + def raise_for_status(self) -> None: ... + def json(self) -> dict[str, object]: ... + + +class AsyncHttpClient(Protocol): + """Protocol for async HTTP client.""" + + async def post( + self, + url: str, + *, + json: dict[str, object] | None = None + ) -> HttpResponse: ... + + async def aclose(self) -> None: ... + + +class LlmResponse(TypedDict): + """Type for LLM API response structure.""" + choices: list[dict[str, object]] + + +class LlmChoice(TypedDict): + """Type for individual choice in LLM response.""" + message: dict[str, object] + + +class LlmMessage(TypedDict): + """Type for message in LLM choice.""" + content: str + + class DocumentMetadata(TypedDict, total=False): """Structured metadata for documents.""" @@ -27,7 +62,7 @@ class MetadataTagger: endpoint: str model: str - client: httpx.AsyncClient + client: AsyncHttpClient def __init__( self, @@ -60,7 +95,10 @@ class MetadataTagger: if api_key: headers["Authorization"] = f"Bearer {api_key}" - self.client = httpx.AsyncClient(timeout=60.0, headers=headers) + # Create client with proper typing - httpx.AsyncClient implements AsyncHttpClient protocol + AsyncClientClass = getattr(httpx, "AsyncClient") + raw_client = AsyncClientClass(timeout=60.0, headers=headers) + self.client = cast(AsyncHttpClient, raw_client) async def tag_document( self, document: Document, custom_instructions: str | None = None @@ -87,29 +125,52 @@ class MetadataTagger: ) # Merge with existing metadata - preserve ALL existing fields and add LLM-generated ones - from typing import cast from ..core.models import DocumentMetadata as CoreDocumentMetadata # Start with a copy of existing metadata to preserve all fields - # Cast to avoid TypedDict key errors during manipulation - updated_metadata = cast(dict[str, object], dict(document.metadata)) + updated_metadata = dict(document.metadata) # Update/enhance with LLM-generated metadata, preserving existing values when new ones are empty - if metadata.get("title") and not updated_metadata.get("title"): - updated_metadata["title"] = str(metadata["title"]) # type: ignore[typeddict-item] - if metadata.get("summary") and not updated_metadata.get("description"): - updated_metadata["description"] = str(metadata["summary"]) + if title_val := metadata.get("title"): + if not updated_metadata.get("title") and isinstance(title_val, str): + updated_metadata["title"] = title_val + if summary_val := metadata.get("summary"): + if not updated_metadata.get("description"): + updated_metadata["description"] = str(summary_val) # Ensure required fields have values - updated_metadata.setdefault("source_url", "") - updated_metadata.setdefault("timestamp", datetime.now(UTC)) - updated_metadata.setdefault("content_type", "text/plain") - updated_metadata.setdefault("word_count", len(document.content.split())) - updated_metadata.setdefault("char_count", len(document.content)) + _ = updated_metadata.setdefault("source_url", "") + _ = updated_metadata.setdefault("timestamp", datetime.now(UTC)) + _ = updated_metadata.setdefault("content_type", "text/plain") + _ = updated_metadata.setdefault("word_count", len(document.content.split())) + _ = updated_metadata.setdefault("char_count", len(document.content)) - # Cast to the expected type since we're preserving all fields from the original metadata - document.metadata = cast(CoreDocumentMetadata, updated_metadata) + # Build a proper DocumentMetadata instance with only valid keys + new_metadata: CoreDocumentMetadata = { + "source_url": str(updated_metadata.get("source_url", "")), + "timestamp": ( + lambda ts: ts if isinstance(ts, datetime) else datetime.now(UTC) + )(updated_metadata.get("timestamp", datetime.now(UTC))), + "content_type": str(updated_metadata.get("content_type", "text/plain")), + "word_count": (lambda wc: int(wc) if isinstance(wc, (int, str)) else 0)(updated_metadata.get("word_count", 0)), + "char_count": (lambda cc: int(cc) if isinstance(cc, (int, str)) else 0)(updated_metadata.get("char_count", 0)), + } + + # Add optional fields if they exist + if "title" in updated_metadata and updated_metadata["title"]: + new_metadata["title"] = str(updated_metadata["title"]) + if "description" in updated_metadata and updated_metadata["description"]: + new_metadata["description"] = str(updated_metadata["description"]) + if "tags" in updated_metadata and isinstance(updated_metadata["tags"], list): + tags_list = cast(list[object], updated_metadata["tags"]) + new_metadata["tags"] = [str(tag) for tag in tags_list if tag is not None] + if "category" in updated_metadata and updated_metadata["category"]: + new_metadata["category"] = str(updated_metadata["category"]) + if "language" in updated_metadata and updated_metadata["language"]: + new_metadata["language"] = str(updated_metadata["language"]) + + document.metadata = new_metadata return document @@ -199,27 +260,27 @@ Return a JSON object with the following structure: if not isinstance(result_raw, dict): raise IngestionError("Invalid response format from LLM") - result = cast(dict[str, object], result_raw) + result = cast(LlmResponse, result_raw) # Extract content from response choices = result.get("choices", []) if not choices or not isinstance(choices, list): raise IngestionError("No response from LLM") - first_choice_raw = cast(object, choices[0]) + first_choice_raw = choices[0] if not isinstance(first_choice_raw, dict): raise IngestionError("Invalid choice format") - first_choice = cast(dict[str, object], first_choice_raw) + first_choice = cast(LlmChoice, first_choice_raw) message_raw = first_choice.get("message", {}) if not isinstance(message_raw, dict): raise IngestionError("Invalid message format") - message = cast(dict[str, object], message_raw) + message = cast(LlmMessage, message_raw) content_str = str(message.get("content", "{}")) try: - raw_metadata = json.loads(content_str) + raw_metadata = cast(dict[str, object], json.loads(content_str)) except json.JSONDecodeError as e: raise IngestionError(f"Failed to parse LLM response: {e}") from e diff --git a/ingest_pipeline/utils/vectorizer.py b/ingest_pipeline/utils/vectorizer.py index b7e6383..75a5e14 100644 --- a/ingest_pipeline/utils/vectorizer.py +++ b/ingest_pipeline/utils/vectorizer.py @@ -51,7 +51,7 @@ class Vectorizer: if api_key: headers["Authorization"] = f"Bearer {api_key}" - self.client = httpx.AsyncClient(timeout=60.0, headers=headers) # type: ignore[attr-defined] + self.client: httpx.AsyncClient = httpx.AsyncClient(timeout=60.0, headers=headers) async def vectorize(self, text: str) -> list[float]: """ diff --git a/logs/tui.log b/logs/tui.log index 6e5bd8b..f8973bf 100644 --- a/logs/tui.log +++ b/logs/tui.log @@ -846,3 +846,29 @@ metadata.author 2025-09-18 08:29:04 | INFO | httpx | HTTP Request: POST http://prefect.lab/api/flow_runs/3d6f8223-0b7e-43fd-a1f4-c102f3fc8919/set_state "HTTP/1.1 201 Created" 2025-09-18 08:29:04 | INFO | prefect.flow_runs | Finished in state Completed() 2025-09-18 08:29:06 | INFO | httpx | HTTP Request: POST http://prefect.lab/api/logs/ "HTTP/1.1 201 Created" +2025-09-18 22:21:09 | INFO | ingest_pipeline.cli.tui.utils.runners | Initializing collection management TUI +2025-09-18 22:21:09 | INFO | ingest_pipeline.cli.tui.utils.runners | Scanning available storage backends +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://weaviate.yo/v1/.well-known/openid-configuration "HTTP/1.1 404 Not Found" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://weaviate.yo/v1/meta "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET https://pypi.org/pypi/weaviate-client/json "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://weaviate.yo/v1/schema "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://chat.lab/api/v1/knowledge/list "HTTP/1.1 401 Unauthorized" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | ingest_pipeline.cli.tui.utils.runners | weaviate connected successfully +2025-09-18 22:21:09 | WARNING | ingest_pipeline.cli.tui.utils.runners | open_webui connection failed +2025-09-18 22:21:09 | INFO | ingest_pipeline.cli.tui.utils.runners | r2r connected successfully +2025-09-18 22:21:09 | INFO | ingest_pipeline.cli.tui.utils.runners | Launching TUI with 2 backend(s): weaviate, r2r +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://weaviate.yo/v1/schema "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: POST http://weaviate.yo/v1/graphql "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: POST http://weaviate.yo/v1/graphql "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: POST http://weaviate.yo/v1/graphql "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 22:21:09 | INFO | httpx | HTTP Request: GET http://r2r.lab/v3/collections "HTTP/1.1 200 OK" +2025-09-18 23:06:37 | INFO | ingest_pipeline.cli.tui.utils.runners | Shutting down storage connections +2025-09-18 23:06:37 | INFO | ingest_pipeline.cli.tui.utils.runners | All storage connections closed gracefully diff --git a/prefect.yaml b/prefect.yaml new file mode 100644 index 0000000..628b657 --- /dev/null +++ b/prefect.yaml @@ -0,0 +1,154 @@ +# Prefect deployment configuration for RAG Manager +name: rag-manager +prefect-version: 3.0.0 + +# Build steps - prepare the environment +build: + - prefect.deployments.steps.run_shell_script: + id: prepare-environment + script: | + echo "Preparing RAG Manager environment..." + # Ensure virtual environment is activated + source .venv/bin/activate || echo "Virtual environment not found" + # Install dependencies + uv sync --frozen + # Register custom blocks + prefect block register --module ingest_pipeline.core.models + +# Push steps - handle deployment artifacts +push: null + +# Work pool configuration +work_pool: + name: "{{ prefect.variables.work_pool_name | default('default') }}" + work_queue_name: "{{ prefect.variables.work_queue_name | default('default') }}" + job_variables: + env: + # Prefect configuration + PREFECT_API_URL: "{{ $PREFECT_API_URL }}" + PREFECT_API_KEY: "{{ $PREFECT_API_KEY }}" + + # Application configuration from variables + DEFAULT_BATCH_SIZE: "{{ prefect.variables.default_batch_size | default('50') }}" + MAX_CRAWL_DEPTH: "{{ prefect.variables.max_crawl_depth | default('5') }}" + MAX_CRAWL_PAGES: "{{ prefect.variables.max_crawl_pages | default('100') }}" + MAX_CONCURRENT_TASKS: "{{ prefect.variables.max_concurrent_tasks | default('5') }}" + + # Service endpoints from variables + LLM_ENDPOINT: "{{ prefect.variables.llm_endpoint | default('http://llm.lab') }}" + WEAVIATE_ENDPOINT: "{{ prefect.variables.weaviate_endpoint | default('http://weaviate.yo') }}" + OPENWEBUI_ENDPOINT: "{{ prefect.variables.openwebui_endpoint | default('http://chat.lab') }}" + FIRECRAWL_ENDPOINT: "{{ prefect.variables.firecrawl_endpoint | default('http://crawl.lab:30002') }}" + +# Deployment definitions +deployments: + # Web ingestion deployment + - name: web-ingestion + version: "{{ prefect.variables.deployment_version | default('1.0.0') }}" + tags: + - "{{ prefect.variables.environment | default('development') }}" + - web + - ingestion + description: "Automated web content ingestion using Firecrawl" + entrypoint: ingest_pipeline/flows/ingestion.py:create_ingestion_flow + parameters: + source_type: web + storage_backend: "{{ prefect.variables.default_storage_backend | default('weaviate') }}" + validate_first: true + storage_block_name: "{{ prefect.variables.default_storage_block }}" + ingestor_config_block_name: "{{ prefect.variables.default_firecrawl_block }}" + schedule: + interval: "{{ prefect.variables.default_schedule_interval | default(3600) }}" + timezone: UTC + work_pool: + name: "{{ prefect.variables.web_work_pool | default('default') }}" + job_variables: + env: + INGESTION_TYPE: "web" + MAX_PAGES: "{{ prefect.variables.max_crawl_pages | default('100') }}" + + # Repository ingestion deployment + - name: repository-ingestion + version: "{{ prefect.variables.deployment_version | default('1.0.0') }}" + tags: + - "{{ prefect.variables.environment | default('development') }}" + - repository + - ingestion + description: "Automated repository content ingestion using Repomix" + entrypoint: ingest_pipeline/flows/ingestion.py:create_ingestion_flow + parameters: + source_type: repository + storage_backend: "{{ prefect.variables.default_storage_backend | default('weaviate') }}" + validate_first: true + storage_block_name: "{{ prefect.variables.default_storage_block }}" + ingestor_config_block_name: "{{ prefect.variables.default_repomix_block }}" + schedule: null # Manual trigger only + work_pool: + name: "{{ prefect.variables.repo_work_pool | default('default') }}" + job_variables: + env: + INGESTION_TYPE: "repository" + + # R2R specialized deployment + - name: firecrawl-to-r2r + version: "{{ prefect.variables.deployment_version | default('1.0.0') }}" + tags: + - "{{ prefect.variables.environment | default('development') }}" + - firecrawl + - r2r + - specialized + description: "Optimized Firecrawl to R2R ingestion flow" + entrypoint: ingest_pipeline/flows/ingestion.py:firecrawl_to_r2r_flow + parameters: + storage_block_name: "{{ prefect.variables.r2r_storage_block }}" + schedule: + cron: "{{ prefect.variables.r2r_cron_schedule | default('0 2 * * *') }}" + timezone: UTC + work_pool: + name: "{{ prefect.variables.r2r_work_pool | default('default') }}" + job_variables: + env: + INGESTION_TYPE: "r2r" + SPECIALIZED_FLOW: "true" + +# Automation definitions (commented out - would be created via API) +# automations: +# - name: Cancel Long Running Flows +# description: Cancels flows running longer than 30 minutes +# trigger: +# type: event +# posture: Proactive +# expect: [prefect.flow-run.Running] +# match_related: +# prefect.resource.role: flow +# prefect.resource.name: ingestion_pipeline +# threshold: 1 +# within: 1800 +# actions: +# - type: cancel-flow-run +# source: inferred +# enabled: true + +# Variables that should be set for optimal operation +# Use: prefect variable set +# Required variables: +# - default_storage_backend: weaviate|open_webui|r2r +# - llm_endpoint: URL for LLM service +# - weaviate_endpoint: URL for Weaviate instance +# - openwebui_endpoint: URL for OpenWebUI instance +# - firecrawl_endpoint: URL for Firecrawl service +# +# Optional variables with defaults: +# - default_batch_size: 50 +# - max_crawl_depth: 5 +# - max_crawl_pages: 100 +# - max_concurrent_tasks: 5 +# - default_schedule_interval: 3600 (1 hour) +# - deployment_version: 1.0.0 +# - environment: development + +# Block types that should be registered: +# - storage-config: Storage backend configurations +# - firecrawl-config: Firecrawl scraping parameters +# - repomix-config: Repository processing settings +# - r2r-config: R2R-specific chunking and graph settings \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c11041c..75fca2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,7 @@ ignore = [ "ingest_pipeline/cli/main.py" = ["B008"] # Typer uses function calls in defaults [tool.mypy] -python_version = "3.11" +python_version = "3.12" strict = true warn_return_any = true warn_unused_configs = true @@ -73,6 +73,18 @@ ignore_missing_imports = true # Allow AsyncGenerator types in overrides disable_error_code = ["override"] +[tool.basedpyright] +include = ["ingest_pipeline"] +exclude = ["**/__pycache__", "**/.pytest_cache", "**/node_modules", ".venv", "build", "dist"] +pythonVersion = "3.12" +venvPath = "." +venv = ".venv" +typeCheckingMode = "standard" +useLibraryCodeForTypes = true +reportMissingTypeStubs = "none" +reportMissingModuleSource = "none" +reportAttributeAccessIssue = "warning" + [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..ecc6385 --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,45 @@ +{ + "include": [ + "ingest_pipeline" + ], + "exclude": [ + "**/__pycache__", + "**/.pytest_cache", + "**/node_modules", + ".venv", + "build", + "dist" + ], + "pythonVersion": "3.12", + "venvPath": ".", + "venv": ".venv", + "typeCheckingMode": "standard", + "useLibraryCodeForTypes": true, + "stubPath": "./typings", + "reportCallInDefaultInitializer": "none", + "reportUnknownVariableType": "warning", + "reportUnknownMemberType": "warning", + "reportUnknownArgumentType": "warning", + "reportUnknownLambdaType": "warning", + "reportUnknownParameterType": "warning", + "reportMissingParameterType": "warning", + "reportUnannotatedClassAttribute": "warning", + "reportMissingTypeStubs": "none", + "reportMissingModuleSource": "none", + "reportImportCycles": "none", + "reportUnusedImport": "warning", + "reportUnusedClass": "warning", + "reportUnusedFunction": "warning", + "reportUnusedVariable": "warning", + "reportDuplicateImport": "warning", + "reportWildcardImportFromLibrary": "warning", + "reportAny": "warning", + "reportUnusedCallResult": "none", + "reportUnnecessaryIsInstance": "none", + "reportImplicitOverride": "none", + "reportDeprecated": "warning", + "reportIncompatibleMethodOverride": "error", + "reportIncompatibleVariableOverride": "error", + "reportInconsistentConstructor": "none", + "analyzeUnannotatedFunctions": true +} \ No newline at end of file diff --git a/repomix-output.xml b/repomix-output.xml index 5c1a3d5..3806b2f 100644 --- a/repomix-output.xml +++ b/repomix-output.xml @@ -45,6 +45,8 @@ The content is organized as follows: ingest_pipeline/ + automations/ + __init__.py cli/ tui/ screens/ @@ -109,13 +111,77 @@ ingest_pipeline/ This section contains the contents of the repository's files. + +"""Prefect Automations for ingestion pipeline monitoring and management.""" + +# Automation configurations as YAML-ready dictionaries +AUTOMATION_TEMPLATES = { + "cancel_long_running": """ +name: Cancel Long Running Ingestion Flows +description: Cancels ingestion flows running longer than 30 minutes +trigger: + type: event + posture: Proactive + expect: [prefect.flow-run.Running] + match_related: + prefect.resource.role: flow + prefect.resource.name: ingestion_pipeline + threshold: 1 + within: 1800 +actions: + - type: cancel-flow-run + source: inferred +enabled: true +""", + + "retry_failed": """ +name: Retry Failed Ingestion Flows +description: Retries failed ingestion flows with original parameters +trigger: + type: event + posture: Reactive + expect: [prefect.flow-run.Failed] + match_related: + prefect.resource.role: flow + prefect.resource.name: ingestion_pipeline + threshold: 1 + within: 0 +actions: + - type: run-deployment + source: inferred + parameters: + validate_first: false +enabled: true +""", + + "resource_monitoring": """ +name: Manage Work Pool Based on Resources +description: Pauses work pool when system resources are constrained +trigger: + type: event + posture: Reactive + expect: [system.resource.high_usage] + threshold: 1 + within: 120 +actions: + - type: pause-work-pool + work_pool_name: default +enabled: true +""", +} + + +def get_automation_yaml_templates() -> dict[str, str]: + """Get automation templates as YAML strings.""" + return AUTOMATION_TEMPLATES.copy() + + """Base screen classes for common CRUD patterns.""" from __future__ import annotations -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Generic, TypeVar +from typing import TYPE_CHECKING, Generic, TypeVar from textual import work from textual.app import ComposeResult @@ -131,16 +197,25 @@ if TYPE_CHECKING: T = TypeVar("T") -class BaseScreen(Screen, ABC): +class BaseScreen(Screen[object]): """Base screen with common functionality.""" - def __init__(self, storage_manager: StorageManager, **kwargs: Any) -> None: + def __init__( + self, + storage_manager: StorageManager, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize base screen.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes) + # Ignore any additional kwargs to avoid type issues self.storage_manager = storage_manager -class CRUDScreen(BaseScreen, Generic[T], ABC): +class CRUDScreen(BaseScreen, Generic[T]): """Base class for Create/Read/Update/Delete operations.""" BINDINGS = [ @@ -151,9 +226,17 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): Binding("escape", "app.pop_screen", "Back"), ] - def __init__(self, storage_manager: StorageManager, **kwargs: Any) -> None: + def __init__( + self, + storage_manager: StorageManager, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize CRUD screen.""" - super().__init__(storage_manager, **kwargs) + super().__init__(storage_manager, name=name, id=id, classes=classes) self.items: list[T] = [] self.selected_item: T | None = None self.loading = False @@ -189,35 +272,29 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): table.add_columns(*self.get_table_columns()) return table - @abstractmethod def get_table_columns(self) -> list[str]: """Get table column headers.""" - pass + raise NotImplementedError("Subclasses must implement get_table_columns") - @abstractmethod async def load_items(self) -> list[T]: """Load items from storage.""" - pass + raise NotImplementedError("Subclasses must implement load_items") - @abstractmethod def item_to_row(self, item: T) -> list[str]: """Convert item to table row.""" - pass + raise NotImplementedError("Subclasses must implement item_to_row") - @abstractmethod async def create_item_dialog(self) -> T | None: """Show create item dialog.""" - pass + raise NotImplementedError("Subclasses must implement create_item_dialog") - @abstractmethod async def edit_item_dialog(self, item: T) -> T | None: """Show edit item dialog.""" - pass + raise NotImplementedError("Subclasses must implement edit_item_dialog") - @abstractmethod async def delete_item(self, item: T) -> bool: """Delete item.""" - pass + raise NotImplementedError("Subclasses must implement delete_item") def on_mount(self) -> None: """Initialize screen.""" @@ -288,17 +365,21 @@ class CRUDScreen(BaseScreen, Generic[T], ABC): self.refresh_items() -class ListScreen(BaseScreen, Generic[T], ABC): +class ListScreen(BaseScreen, Generic[T]): """Base for paginated list views.""" def __init__( self, storage_manager: StorageManager, page_size: int = 20, - **kwargs: Any, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object, ) -> None: """Initialize list screen.""" - super().__init__(storage_manager, **kwargs) + super().__init__(storage_manager, name=name, id=id, classes=classes) self.page_size = page_size self.current_page = 0 self.total_items = 0 @@ -316,25 +397,21 @@ class ListScreen(BaseScreen, Generic[T], ABC): classes="list-container", ) - @abstractmethod def get_title(self) -> str: """Get screen title.""" - pass + raise NotImplementedError("Subclasses must implement get_title") - @abstractmethod def create_filters(self) -> Container: """Create filter widgets.""" - pass + raise NotImplementedError("Subclasses must implement create_filters") - @abstractmethod - def create_list_view(self) -> Any: + def create_list_view(self) -> object: """Create list view widget.""" - pass + raise NotImplementedError("Subclasses must implement create_list_view") - @abstractmethod async def load_page(self, page: int, page_size: int) -> tuple[list[T], int]: """Load page of items.""" - pass + raise NotImplementedError("Subclasses must implement load_page") def create_pagination(self) -> Container: """Create pagination controls.""" @@ -361,10 +438,9 @@ class ListScreen(BaseScreen, Generic[T], ABC): finally: self.set_loading(False) - @abstractmethod async def update_list_view(self) -> None: """Update list view with current items.""" - pass + raise NotImplementedError("Subclasses must implement update_list_view") def update_pagination_info(self) -> None: """Update pagination information.""" @@ -397,7 +473,7 @@ class ListScreen(BaseScreen, Generic[T], ABC): self.load_current_page() -class FormScreen(ModalScreen[T], Generic[T], ABC): +class FormScreen(ModalScreen[T], Generic[T]): """Base for input forms with validation.""" BINDINGS = [ @@ -406,9 +482,18 @@ class FormScreen(ModalScreen[T], Generic[T], ABC): Binding("enter", "save", "Save"), ] - def __init__(self, item: T | None = None, **kwargs: Any) -> None: + def __init__( + self, + item: T | None = None, + *, + name: str | None = None, + id: str | None = None, + classes: str | None = None, + **kwargs: object + ) -> None: """Initialize form screen.""" - super().__init__(**kwargs) + super().__init__(name=name, id=id, classes=classes) + # Ignore any additional kwargs to avoid type issues self.item = item self.is_edit_mode = item is not None @@ -427,35 +512,30 @@ class FormScreen(ModalScreen[T], Generic[T], ABC): classes="form-container", ) - @abstractmethod def get_item_type(self) -> str: """Get item type name for title.""" - pass + raise NotImplementedError("Subclasses must implement get_item_type") - @abstractmethod def create_form_fields(self) -> Container: """Create form input fields.""" - pass + raise NotImplementedError("Subclasses must implement create_form_fields") - @abstractmethod def validate_form(self) -> tuple[bool, list[str]]: """Validate form data.""" - pass + raise NotImplementedError("Subclasses must implement validate_form") - @abstractmethod def get_form_data(self) -> T: """Get item from form data.""" - pass + raise NotImplementedError("Subclasses must implement get_form_data") def on_mount(self) -> None: """Initialize form.""" if self.is_edit_mode and self.item: self.populate_form(self.item) - @abstractmethod def populate_form(self, item: T) -> None: """Populate form with item data.""" - pass + raise NotImplementedError("Subclasses must implement populate_form") def action_save(self) -> None: """Save form data.""" @@ -482,6 +562,67 @@ class FormScreen(ModalScreen[T], Generic[T], ABC): self.dismiss(None) + +"""Help screen with keyboard shortcuts and usage information.""" + +from textual.app import ComposeResult +from textual.binding import Binding +from textual.containers import Container, ScrollableContainer +from textual.screen import ModalScreen +from textual.widgets import Button, Markdown, Rule, Static +from typing_extensions import override + + +class HelpScreen(ModalScreen[None]): + """Modern help screen with comprehensive keyboard shortcuts.""" + + help_content: str + + BINDINGS = [ + Binding("escape", "app.pop_screen", "Close"), + Binding("q", "app.pop_screen", "Close"), + Binding("enter", "app.pop_screen", "Close"), + Binding("f1", "app.pop_screen", "Close"), + ] + + def __init__(self, help_content: str): + super().__init__() + self.help_content = help_content + + @override + def compose(self) -> ComposeResult: + with Container(classes="modal-container"): + yield Static("📚 Help & Keyboard Shortcuts", classes="title") + yield Static("Enhanced navigation and productivity features", classes="subtitle") + yield Rule(line_style="heavy") + + with ScrollableContainer(): + yield Markdown(self.help_content) + + yield Container( + Button("✅ Got it! (Press Escape or Enter)", id="close_btn", variant="primary"), + classes="action_buttons center", + ) + + def on_mount(self) -> None: + """Initialize the help screen.""" + # Focus the close button + self.query_one("#close_btn").focus() + + def on_button_pressed(self, event: Button.Pressed) -> None: + """Close help screen.""" + if event.button.id == "close_btn": + self.app.pop_screen() + + + +"""Utility functions for the TUI.""" + +from .runners import dashboard, run_textual_tui + +__all__ = ["dashboard", "run_textual_tui"] + + """Storage management utilities for TUI applications.""" @@ -498,10 +639,11 @@ from ..models import CollectionInfo, StorageCapabilities if TYPE_CHECKING: from ....config.settings import Settings -from ....storage.weaviate import WeaviateStorage -from ....storage.r2r.storage import R2RStorage -from ....storage.openwebui import OpenWebUIStorage + from ....storage.base import BaseStorage +from ....storage.openwebui import OpenWebUIStorage +from ....storage.r2r.storage import R2RStorage +from ....storage.weaviate import WeaviateStorage class StorageBackendProtocol(Protocol): @@ -877,18 +1019,20 @@ class StorageManager: def _get_collection_type(self, collection_name: str, backend: StorageBackend) -> str: """Determine collection type based on name and backend.""" - name_lower = collection_name.lower() - - if "web" in name_lower or "doc" in name_lower: - return "documentation" - elif "repo" in name_lower or "code" in name_lower: - return "repository" - elif backend == StorageBackend.R2R: + # Prioritize definitive backend type first + if backend == StorageBackend.R2R: return "r2r" elif backend == StorageBackend.WEAVIATE: return "weaviate" elif backend == StorageBackend.OPEN_WEBUI: return "openwebui" + + # Fallback to name-based guessing if backend is not specific + name_lower = collection_name.lower() + if "web" in name_lower or "doc" in name_lower: + return "documentation" + elif "repo" in name_lower or "code" in name_lower: + return "repository" else: return "general" @@ -976,6 +1120,52 @@ class StorageManager: return self.has_capability(backend, StorageCapabilities.FULL_FEATURED) + +"""Enhanced widgets with keyboard navigation support.""" + +from .cards import MetricsCard +from .indicators import EnhancedProgressBar, StatusIndicator +from .tables import EnhancedDataTable + +__all__ = [ + "MetricsCard", + "StatusIndicator", + "EnhancedProgressBar", + "EnhancedDataTable", +] + + + +"""Metrics card widget.""" + +from typing import Any + +from textual.app import ComposeResult +from textual.widgets import Static +from typing_extensions import override + + +class MetricsCard(Static): + """A modern metrics display card.""" + + title: str + value: str + description: str + + def __init__(self, title: str, value: str, description: str = "", **kwargs: Any) -> None: + super().__init__(**kwargs) + self.title = title + self.value = value + self.description = description + + @override + def compose(self) -> ComposeResult: + yield Static(self.value, classes="metrics-value") + yield Static(self.title, classes="metrics-label") + if self.description: + yield Static(self.description, classes="metrics-description") + + """Firecrawl configuration widgets for advanced scraping options.""" @@ -1583,7 +1773,7 @@ class FirecrawlConfigWidget(Container): def on_button_pressed(self, event: Button.Pressed) -> None: """Handle button presses.""" - if event.button.id.startswith("tab_"): + if event.button.id and event.button.id.startswith("tab_"): tab_name = event.button.id[4:] # Remove "tab_" prefix self.show_tab(tab_name) @@ -1601,15 +1791,15 @@ class FirecrawlConfigWidget(Container): pass elif self.current_tab == "map": try: - form = self.query_one("#map_form", MapOptionsForm) - map_opts = form.get_map_options() + map_form = self.query_one("#map_form", MapOptionsForm) + map_opts = map_form.get_map_options() options.update(cast(FirecrawlOptions, map_opts)) except Exception: pass elif self.current_tab == "extract": try: - form = self.query_one("#extract_form", ExtractOptionsForm) - extract_opts = form.get_extract_options() + extract_form = self.query_one("#extract_form", ExtractOptionsForm) + extract_opts = extract_form.get_extract_options() options.update(cast(FirecrawlOptions, extract_opts)) except Exception: pass @@ -1718,8 +1908,8 @@ class ChunkViewer(Container): "id": str(chunk_data.get("id", "")), "document_id": self.document_id, "content": str(chunk_data.get("text", "")), - "start_index": int(chunk_data.get("start_index", 0)), - "end_index": int(chunk_data.get("end_index", 0)), + "start_index": (lambda si: int(si) if isinstance(si, (int, str)) else 0)(chunk_data.get("start_index", 0)), + "end_index": (lambda ei: int(ei) if isinstance(ei, (int, str)) else 0)(chunk_data.get("end_index", 0)), "metadata": dict(chunk_data.get("metadata", {})), } self.chunks.append(chunk_info) @@ -1880,7 +2070,7 @@ class EntityGraph(Container): tree.root.expand() - def on_tree_node_selected(self, event: Tree.NodeSelected) -> None: + def on_tree_node_selected(self, event: Tree.NodeSelected[EntityInfo]) -> None: """Handle entity selection.""" if hasattr(event.node, "data") and event.node.data: entity = event.node.data @@ -2129,6 +2319,151 @@ class DocumentOverview(Container): doc_table.add_row("Error", str(e)) + +"""Enhanced DataTable with improved keyboard navigation.""" + +from typing import Any + +from textual import events +from textual.binding import Binding +from textual.message import Message +from textual.widgets import DataTable + + +class EnhancedDataTable(DataTable[Any]): + """DataTable with enhanced keyboard navigation and visual feedback.""" + + BINDINGS = [ + Binding("up,k", "cursor_up", "Cursor Up", show=False), + Binding("down,j", "cursor_down", "Cursor Down", show=False), + Binding("left,h", "cursor_left", "Cursor Left", show=False), + Binding("right,l", "cursor_right", "Cursor Right", show=False), + Binding("home", "cursor_home", "First Row", show=False), + Binding("end", "cursor_end", "Last Row", show=False), + Binding("pageup", "page_up", "Page Up", show=False), + Binding("pagedown", "page_down", "Page Down", show=False), + Binding("enter", "select_cursor", "Select", show=False), + Binding("space", "toggle_selection", "Toggle Selection", show=False), + Binding("ctrl+a", "select_all", "Select All", show=False), + Binding("ctrl+shift+a", "clear_selection", "Clear Selection", show=False), + ] + + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.cursor_type = "row" # Default to row selection + self.zebra_stripes = True # Enable zebra striping for better visibility + self.show_cursor = True + + def on_key(self, event: events.Key) -> None: + """Handle additional keyboard shortcuts.""" + if event.key == "ctrl+1": + # Jump to first column + self.move_cursor(column=0) + event.prevent_default() + elif event.key == "ctrl+9": + # Jump to last column + if self.columns: + self.move_cursor(column=len(self.columns) - 1) + event.prevent_default() + elif event.key == "/": + # Start quick search (to be implemented by parent) + self.post_message(self.QuickSearch(self)) + event.prevent_default() + elif event.key == "escape": + # Clear selection or exit search + # Clear selection by calling action + self.action_clear_selection() + event.prevent_default() + # No else clause needed - just handle our events + + def action_cursor_home(self) -> None: + """Move cursor to first row.""" + if self.row_count > 0: + self.move_cursor(row=0) + + def action_cursor_end(self) -> None: + """Move cursor to last row.""" + if self.row_count > 0: + self.move_cursor(row=self.row_count - 1) + + def action_page_up(self) -> None: + """Move cursor up by visible page size.""" + if self.row_count > 0: + page_size = max(1, self.size.height // 2) # Approximate visible rows + new_row = max(0, self.cursor_coordinate.row - page_size) + self.move_cursor(row=new_row) + + def action_page_down(self) -> None: + """Move cursor down by visible page size.""" + if self.row_count > 0: + page_size = max(1, self.size.height // 2) # Approximate visible rows + new_row = min(self.row_count - 1, self.cursor_coordinate.row + page_size) + self.move_cursor(row=new_row) + + def action_toggle_selection(self) -> None: + """Toggle selection of current row.""" + if self.row_count > 0: + current_row = self.cursor_coordinate.row + # This will be handled by the parent screen + self.post_message(self.RowToggled(self, current_row)) + + def action_select_all(self) -> None: + """Select all rows.""" + # This will be handled by the parent screen + self.post_message(self.SelectAll(self)) + + def action_clear_selection(self) -> None: + """Clear all selections.""" + # This will be handled by the parent screen + self.post_message(self.ClearSelection(self)) + + # Custom messages for enhanced functionality + class QuickSearch(Message): + """Posted when user wants to start a quick search.""" + + def __init__(self, table: "EnhancedDataTable") -> None: + super().__init__() + self.table = table + + class RowToggled(Message): + """Posted when a row selection is toggled.""" + + def __init__(self, table: "EnhancedDataTable", row_index: int) -> None: + super().__init__() + self.table = table + self.row_index = row_index + + class SelectAll(Message): + """Posted when user wants to select all rows.""" + + def __init__(self, table: "EnhancedDataTable") -> None: + super().__init__() + self.table = table + + class ClearSelection(Message): + """Posted when user wants to clear selection.""" + + def __init__(self, table: "EnhancedDataTable") -> None: + super().__init__() + self.table = table + + + +"""Enhanced TUI package with keyboard navigation and modular architecture.""" + +from .app import CollectionManagementApp +from .models import CollectionInfo, DocumentInfo +from .utils import dashboard, run_textual_tui + +__all__ = [ + "CollectionManagementApp", + "CollectionInfo", + "DocumentInfo", + "dashboard", + "run_textual_tui", +] + + """Responsive layout system for TUI applications.""" @@ -2295,12 +2630,12 @@ class CollapsibleSidebar(Container): else: _ = self.remove_class("collapsed") - def expand(self) -> None: + def expand_sidebar(self) -> None: """Expand sidebar.""" if self.collapsed: self.toggle() - def collapse(self) -> None: + def collapse_sidebar(self) -> None: """Collapse sidebar.""" if not self.collapsed: self.toggle() @@ -2512,6 +2847,90 @@ class SplitPane(Container): self.query_one(f".{self.__class__.__name__} .right-pane").styles.width = f"{(1 - self.split_ratio) * 100}%" + +"""CLI module for the ingestion pipeline.""" + +from .main import app + +__all__ = ["app"] + + + +"""Core module for ingestion pipeline.""" + +from .exceptions import ( + IngestionError, + StorageError, + VectorizationError, +) +from .models import ( + Document, + IngestionJob, + IngestionResult, + IngestionSource, + IngestionStatus, + StorageBackend, +) + +__all__ = [ + "Document", + "IngestionJob", + "IngestionResult", + "IngestionSource", + "IngestionStatus", + "StorageBackend", + "IngestionError", + "StorageError", + "VectorizationError", +] + + + +"""Custom exceptions for the ingestion pipeline.""" + + +class IngestionError(Exception): + """Base exception for ingestion errors.""" + + pass + + +class StorageError(IngestionError): + """Exception for storage-related errors.""" + + pass + + +class VectorizationError(IngestionError): + """Exception for vectorization errors.""" + + pass + + +class ConfigurationError(IngestionError): + """Exception for configuration errors.""" + + pass + + +class SourceNotFoundError(IngestionError): + """Exception when source cannot be found or accessed.""" + + pass + + + +"""Prefect flows for orchestration.""" + +from .ingestion import create_ingestion_flow +from .scheduler import create_scheduled_deployment + +__all__ = [ + "create_ingestion_flow", + "create_scheduled_deployment", +] + + """R2R storage package providing comprehensive R2R integration.""" @@ -2989,6 +3408,7 @@ class R2RCollections: from __future__ import annotations +import asyncio import contextlib from collections.abc import AsyncGenerator, Iterable, Mapping, Sequence from datetime import UTC, datetime @@ -2997,17 +3417,20 @@ from uuid import UUID, uuid4 import httpx from r2r import R2RAsyncClient + +try: + from r2r import R2RException +except ImportError: + # Fallback if R2RException is not available in this version + class R2RException(Exception): + """Fallback exception for R2R operations.""" + pass from typing_extensions import override from ...core.exceptions import StorageError from ...core.models import Document, DocumentMetadata, IngestionSource, StorageConfig from ..base import BaseStorage - -class R2RClientException(Exception): - """Exception raised by R2R client operations.""" - - T = TypeVar("T") @@ -3064,15 +3487,26 @@ class R2RStorage(BaseStorage): def __init__(self, config: StorageConfig) -> None: """Initialize R2R storage with SDK client.""" super().__init__(config) - self.client: R2RAsyncClient = R2RAsyncClient(str(config.endpoint)) + self.endpoint: str = str(config.endpoint).rstrip("/") + self.client: R2RAsyncClient = R2RAsyncClient(self.endpoint) self.default_collection_id: str | None = None @override async def initialize(self) -> None: """Initialize R2R connection and ensure default collection exists.""" try: + # Ensure we have an event loop + try: + asyncio.get_running_loop() + except RuntimeError: + # No event loop running, this should not happen in async context + # but let's be defensive + import logging + + logging.warning("No event loop found during R2R initialization") + # Test connection using direct HTTP call to v3 API - endpoint = str(self.config.endpoint).rstrip("/") + endpoint = self.endpoint client = httpx.AsyncClient() try: response = await client.get(f"{endpoint}/v3/collections") @@ -3086,7 +3520,7 @@ class R2RStorage(BaseStorage): async def _ensure_collection(self, collection_name: str) -> str: """Get or create collection by name.""" try: - endpoint = str(self.config.endpoint).rstrip("/") + endpoint = self.endpoint client = httpx.AsyncClient() try: # List collections and find by name @@ -3135,47 +3569,330 @@ class R2RStorage(BaseStorage): self, documents: list[Document], *, collection_name: str | None = None ) -> list[str]: """Store multiple documents.""" - collection_id = self.default_collection_id - if collection_name and collection_name != self.config.collection_name: + # Fix: Always ensure we have the correct collection ID + if collection_name: + # If a specific collection is requested, get its ID collection_id = await self._ensure_collection(collection_name) + else: + # If no collection specified, use the default one from config + if self.default_collection_id: + collection_id = self.default_collection_id + else: + # Fallback: ensure the default collection exists + collection_id = await self._ensure_collection(self.config.collection_name) + self.default_collection_id = collection_id + + print( + f"Using collection ID: {collection_id} for collection: {collection_name or self.config.collection_name}" + ) stored_ids: list[str] = [] + failed_documents: list[Document] = [] for document in documents: try: - # Create document - doc_response = await self.client.documents.create( - raw_text=document.content, - metadata=self._build_metadata(document), - id=str(document.id), - ) - response_payload = getattr(doc_response, "results", doc_response) - doc_id = _extract_id(response_payload, str(document.id)) + # Create document with explicit ID using direct HTTP call + requested_id = str(document.id) + print(f"Creating document with ID: {requested_id}") - # Add to collection - if collection_id: - _ = await self.client.collections.add_document(collection_id, doc_id) + # Validate document before sending to R2R + if not document.content or not document.content.strip(): + print(f"Skipping document {requested_id}: empty content") + failed_documents.append(document) + continue - stored_ids.append(doc_id) + if len(document.content) > 1_000_000: # 1MB limit + print( + f"Skipping document {requested_id}: content too large ({len(document.content)} chars)" + ) + failed_documents.append(document) + continue + + # Use direct HTTP call with proper multipart form-data format + import asyncio + import json + + max_retries = 3 + retry_delay = 1.0 + doc_response = None # Initialize variable to avoid UnboundLocalError + + for attempt in range(max_retries): + try: + async with httpx.AsyncClient() as http_client: + # Use files parameter but with string values for multipart/form-data + # This matches the cURL -F behavior more closely + metadata = self._build_metadata(document) + print(f"Built metadata for document {requested_id}: {metadata}") + + files = { + "raw_text": (None, document.content), + "metadata": (None, json.dumps(metadata)), + "id": (None, requested_id), + "ingestion_mode": (None, "hi-res"), # Enable R2R enrichment + } + + # Add collection_ids if we have a collection to assign to + if collection_id: + files["collection_ids"] = (None, json.dumps([collection_id])) + print( + f"Creating document {requested_id} with collection_ids: [{collection_id}]" + ) + + print(f"Sending to R2R - files keys: {list(files.keys())}") + print(f"Metadata JSON: {files['metadata'][1]}") + + response = await http_client.post( + f"{self.endpoint}/v3/documents", + files=files, + ) + + if response.status_code == 422: + # Get detailed error information for 422 responses + try: + error_detail = response.json() + print( + f"R2R validation error for document {requested_id}: {error_detail}" + ) + print(f"Document content length: {len(document.content)}") + print(f"Document metadata sent: {metadata}") + print(f"Response status: {response.status_code}") + print(f"Response headers: {dict(response.headers)}") + except Exception: + print( + f"R2R validation error for document {requested_id}: {response.text}" + ) + print(f"Document metadata sent: {metadata}") + # Don't retry validation errors + break + + if response.status_code >= 500 and attempt < max_retries - 1: + print( + f"Server error {response.status_code} for document {requested_id}, retrying in {retry_delay}s..." + ) + await asyncio.sleep(retry_delay) + retry_delay *= 2 # Exponential backoff + continue + + response.raise_for_status() + doc_response = response.json() + break # Success - exit retry loop + + except httpx.TimeoutException: + if attempt >= max_retries - 1: + raise + print( + f"Timeout for document {requested_id}, retrying in {retry_delay}s..." + ) + await asyncio.sleep(retry_delay) + retry_delay *= 2 + continue + except httpx.HTTPStatusError as e: + if ( + e.response.status_code < 500 + or attempt >= max_retries - 1 + ): + raise + + print( + f"Server error {e.response.status_code} for document {requested_id}, retrying in {retry_delay}s..." + ) + await asyncio.sleep(retry_delay) + retry_delay *= 2 + # Only process response if we have a successful doc_response + if doc_response is not None: + response_payload = doc_response.get("results", doc_response) + doc_id = _extract_id(response_payload, requested_id) + + print(f"R2R returned document ID: {doc_id}") + + # Verify the ID matches what we requested + if doc_id != requested_id: + print(f"Warning: Requested ID {requested_id} but got {doc_id}") + + # Collection assignment is now handled during document creation + # No need to add to collection afterward if collection_ids was provided + if collection_id: + print( + f"Document {doc_id} should be assigned to collection {collection_id} via creation API" + ) + + stored_ids.append(doc_id) + else: + print(f"No successful response received for document {requested_id}") + failed_documents.append(document) except Exception as exc: print(f"Failed to store document {document.id}: {exc}") + failed_documents.append(document) + + # Log specific error types for debugging + if "422" in str(exc): + print(" → Data validation issue - check document content and metadata format") + elif "timeout" in str(exc).lower(): + print(" → Network timeout - R2R may be overloaded") + elif "500" in str(exc): + print(" → Server error - R2R internal issue") + else: + import traceback + + traceback.print_exc() continue + # Phase 2: Append rich metadata to successfully stored documents + if stored_ids: + print(f"Enriching metadata for {len(stored_ids)} documents...") + enriched_count = 0 + + for i, doc_id in enumerate(stored_ids): + if i < len(documents): # Ensure we have the corresponding document + document = documents[i] + if rich_metadata := self._extract_rich_metadata(document): + success = await self.append_document_metadata(doc_id, rich_metadata) + if success: + enriched_count += 1 + + print(f"Successfully enriched metadata for {enriched_count}/{len(stored_ids)} documents") + return stored_ids + def _extract_rich_metadata(self, document: Document) -> dict[str, object]: + """Extract metadata fields that R2R filters out during hi-res ingestion.""" + metadata = document.metadata + + # Extract fields that typically get filtered by R2R's hi-res mode + rich_metadata: dict[str, object] = {} + + # Authorship and source info + if author := metadata.get("author"): + if author != "Unknown": # Don't append default values + rich_metadata["author"] = author + if domain := metadata.get("domain"): + rich_metadata["domain"] = domain + if site_name := metadata.get("site_name"): + rich_metadata["site_name"] = site_name + + # Content categorization + if tags := metadata.get("tags"): + if tags: # Only append non-empty tag lists + rich_metadata["tags"] = tags + if category := metadata.get("category"): + rich_metadata["category"] = category + + # Document structure + if heading_hierarchy := metadata.get("heading_hierarchy"): + if heading_hierarchy: + rich_metadata["heading_hierarchy"] = heading_hierarchy + if section_depth := metadata.get("section_depth"): + rich_metadata["section_depth"] = section_depth + if has_code_blocks := metadata.get("has_code_blocks"): + rich_metadata["has_code_blocks"] = has_code_blocks + if has_images := metadata.get("has_images"): + rich_metadata["has_images"] = has_images + if has_links := metadata.get("has_links"): + rich_metadata["has_links"] = has_links + + # Processing metadata + if extraction_method := metadata.get("extraction_method"): + rich_metadata["extraction_method"] = extraction_method + if crawl_depth := metadata.get("crawl_depth"): + rich_metadata["crawl_depth"] = crawl_depth + if last_modified := metadata.get("last_modified"): + rich_metadata["last_modified"] = last_modified.isoformat() if last_modified else None + + # Content quality indicators + if readability_score := metadata.get("readability_score"): + rich_metadata["readability_score"] = readability_score + if completeness_score := metadata.get("completeness_score"): + rich_metadata["completeness_score"] = completeness_score + + return rich_metadata + def _build_metadata(self, document: Document) -> dict[str, object]: - """Convert document metadata to R2R format.""" - return { - "title": document.metadata.get("title"), - "source_url": document.metadata["source_url"], - "description": document.metadata.get("description"), - "content_type": document.metadata["content_type"], - "word_count": document.metadata["word_count"], - "char_count": document.metadata["char_count"], + """Convert document metadata to enriched R2R format.""" + metadata = document.metadata + + + # Core required fields + result: dict[str, object] = { + "source_url": metadata["source_url"], + "content_type": metadata["content_type"], + "word_count": metadata["word_count"], + "char_count": metadata["char_count"], + "timestamp": metadata["timestamp"].isoformat(), "ingestion_source": document.source.value, - "timestamp": document.metadata["timestamp"].isoformat(), } + # Basic optional fields + if title := metadata.get("title"): + result["title"] = title + if description := metadata.get("description"): + result["description"] = description + + # Content categorization + if tags := metadata.get("tags"): + result["tags"] = tags + if category := metadata.get("category"): + result["category"] = category + if section := metadata.get("section"): + result["section"] = section + if language := metadata.get("language"): + result["language"] = language + + # Authorship and source info + if author := metadata.get("author"): + result["author"] = author + if domain := metadata.get("domain"): + result["domain"] = domain + if site_name := metadata.get("site_name"): + result["site_name"] = site_name + + # Document structure + if heading_hierarchy := metadata.get("heading_hierarchy"): + result["heading_hierarchy"] = heading_hierarchy + if section_depth := metadata.get("section_depth"): + result["section_depth"] = section_depth + if has_code_blocks := metadata.get("has_code_blocks"): + result["has_code_blocks"] = has_code_blocks + if has_images := metadata.get("has_images"): + result["has_images"] = has_images + if has_links := metadata.get("has_links"): + result["has_links"] = has_links + + # Processing metadata + if extraction_method := metadata.get("extraction_method"): + result["extraction_method"] = extraction_method + if crawl_depth := metadata.get("crawl_depth"): + result["crawl_depth"] = crawl_depth + if last_modified := metadata.get("last_modified"): + result["last_modified"] = last_modified.isoformat() if last_modified else None + + # Content quality indicators + if readability_score := metadata.get("readability_score"): + result["readability_score"] = readability_score + if completeness_score := metadata.get("completeness_score"): + result["completeness_score"] = completeness_score + + # Repository-specific fields + if file_path := metadata.get("file_path"): + result["file_path"] = file_path + if repository_name := metadata.get("repository_name"): + result["repository_name"] = repository_name + if branch_name := metadata.get("branch_name"): + result["branch_name"] = branch_name + if commit_hash := metadata.get("commit_hash"): + result["commit_hash"] = commit_hash + if programming_language := metadata.get("programming_language"): + result["programming_language"] = programming_language + + # Custom business metadata + if importance_score := metadata.get("importance_score"): + result["importance_score"] = importance_score + if review_status := metadata.get("review_status"): + result["review_status"] = review_status + if assigned_team := metadata.get("assigned_team"): + result["assigned_team"] = assigned_team + + return result + @override async def retrieve( self, document_id: str, *, collection_name: str | None = None @@ -3183,14 +3900,18 @@ class R2RStorage(BaseStorage): """Retrieve a document by ID.""" try: response = await self.client.documents.retrieve(document_id) - except R2RClientException: - # Document not found or access denied - expected case - return None - except Exception as e: - # Log unexpected errors for debugging + except R2RException as exc: + status_code = getattr(exc, "status_code", None) + if status_code == 404: + return None import logging - logging.warning(f"Unexpected error retrieving document {document_id}: {e}") + logging.warning(f"Unexpected error retrieving document {document_id}: {exc}") + return None + except Exception as error: + import logging + + logging.warning(f"Unexpected error retrieving document {document_id}: {error}") return None payload = getattr(response, "results", response) return self._convert_to_document(payload, collection_name) @@ -3200,6 +3921,7 @@ class R2RStorage(BaseStorage): doc_map = _as_mapping(r2r_doc) metadata_map = _as_mapping(doc_map.get("metadata", {})) + doc_id_str = _extract_id(r2r_doc, str(uuid4())) try: doc_uuid = UUID(doc_id_str) @@ -3209,15 +3931,79 @@ class R2RStorage(BaseStorage): timestamp = _as_datetime(doc_map.get("created_at", metadata_map.get("timestamp"))) metadata: DocumentMetadata = { + # Core required fields "source_url": str(metadata_map.get("source_url", "")), - "title": cast(str | None, metadata_map.get("title")), - "description": cast(str | None, metadata_map.get("description")), "timestamp": timestamp, "content_type": str(metadata_map.get("content_type", "text/plain")), "word_count": _as_int(metadata_map.get("word_count")), "char_count": _as_int(metadata_map.get("char_count")), } + # Add optional fields if present + # Check for title in both top-level and metadata (R2R schema has title as top-level field) + if title := (doc_map.get("title") or metadata_map.get("title")): + metadata["title"] = cast(str | None, title) + # Check for summary in top-level R2R field (R2R schema has summary as top-level field) + if summary := (doc_map.get("summary") or metadata_map.get("summary")): + metadata["description"] = cast(str | None, summary) + elif description := metadata_map.get("description"): + metadata["description"] = cast(str | None, description) + if tags := metadata_map.get("tags"): + metadata["tags"] = [str(tag) for tag in tags] if isinstance(tags, list) else [] + if category := metadata_map.get("category"): + metadata["category"] = str(category) + if section := metadata_map.get("section"): + metadata["section"] = str(section) + if language := metadata_map.get("language"): + metadata["language"] = str(language) + if author := metadata_map.get("author"): + metadata["author"] = str(author) + if domain := metadata_map.get("domain"): + metadata["domain"] = str(domain) + if site_name := metadata_map.get("site_name"): + metadata["site_name"] = str(site_name) + if heading_hierarchy := metadata_map.get("heading_hierarchy"): + metadata["heading_hierarchy"] = ( + list(heading_hierarchy) if isinstance(heading_hierarchy, list) else [] + ) + if section_depth := metadata_map.get("section_depth"): + metadata["section_depth"] = _as_int(section_depth) + if has_code_blocks := metadata_map.get("has_code_blocks"): + metadata["has_code_blocks"] = bool(has_code_blocks) + if has_images := metadata_map.get("has_images"): + metadata["has_images"] = bool(has_images) + if has_links := metadata_map.get("has_links"): + metadata["has_links"] = bool(has_links) + if extraction_method := metadata_map.get("extraction_method"): + metadata["extraction_method"] = str(extraction_method) + if crawl_depth := metadata_map.get("crawl_depth"): + metadata["crawl_depth"] = _as_int(crawl_depth) + if last_modified := metadata_map.get("last_modified"): + metadata["last_modified"] = _as_datetime(last_modified) + readability_score = metadata_map.get("readability_score") + if readability_score is not None: + if isinstance(readability_score, (int, float)): + metadata["readability_score"] = float(readability_score) + elif isinstance(readability_score, str): + try: + metadata["readability_score"] = float(readability_score) + except ValueError: + metadata["readability_score"] = None + else: + metadata["readability_score"] = None + + completeness_score = metadata_map.get("completeness_score") + if completeness_score is not None: + if isinstance(completeness_score, (int, float)): + metadata["completeness_score"] = float(completeness_score) + elif isinstance(completeness_score, str): + try: + metadata["completeness_score"] = float(completeness_score) + except ValueError: + metadata["completeness_score"] = None + else: + metadata["completeness_score"] = None + source_value = str(metadata_map.get("ingestion_source", IngestionSource.WEB.value)) try: source_enum = IngestionSource(source_value) @@ -3270,10 +4056,12 @@ class R2RStorage(BaseStorage): try: doc_response = await self.client.documents.retrieve(document_id) - except R2RClientException as e: + except R2RException as exc: import logging - logging.warning(f"Failed to retrieve document {document_id} during search: {e}") + logging.warning( + f"Failed to retrieve document {document_id} during search: {exc}" + ) continue document_payload = getattr(doc_response, "results", doc_response) @@ -3298,8 +4086,8 @@ class R2RStorage(BaseStorage): yield document - except R2RClientException as e: - raise StorageError(f"Search failed: {e}") from e + except R2RException as exc: + raise StorageError(f"Search failed: {exc}") from exc @override async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: @@ -3307,14 +4095,14 @@ class R2RStorage(BaseStorage): try: _ = await self.client.documents.delete(document_id) return True - except R2RClientException: + except R2RException: return False @override async def count(self, *, collection_name: str | None = None) -> int: """Get document count in collection.""" try: - endpoint = str(self.config.endpoint).rstrip("/") + endpoint = self.endpoint client = httpx.AsyncClient() try: # Get collections and find the count for the specific collection @@ -3335,7 +4123,28 @@ class R2RStorage(BaseStorage): except Exception: return 0 - @override + async def append_document_metadata(self, document_id: str, metadata: dict[str, object]) -> bool: + """Append metadata to existing document in R2R.""" + try: + # Filter out None values and empty collections to avoid cluttering R2R + filtered_metadata = { + k: v for k, v in metadata.items() + if v is not None and v != [] and v != "" + } + + if not filtered_metadata: + return True # Nothing to append + + await self.client.documents.append_metadata( + id=document_id, + metadata=[filtered_metadata] # API expects list of dicts + ) + print(f"Appended metadata to document {document_id}: {list(filtered_metadata.keys())}") + return True + except Exception as e: + print(f"Failed to append metadata to {document_id}: {e}") + return False + async def close(self) -> None: """Close R2R client.""" try: @@ -3366,8 +4175,8 @@ class R2RStorage(BaseStorage): response = await self.client.collections.create(name=name, description=description) created = _as_mapping(getattr(response, "results", {})) return str(created.get("id", name)) - except R2RClientException as e: - raise StorageError(f"Failed to create collection {name}: {e}") from e + except R2RException as exc: + raise StorageError(f"Failed to create collection {name}: {exc}") from exc async def delete_collection(self, collection_name: str) -> bool: """Delete a collection.""" @@ -3375,14 +4184,14 @@ class R2RStorage(BaseStorage): collection_id = await self._ensure_collection(collection_name) _ = await self.client.collections.delete(collection_id) return True - except R2RClientException: + except R2RException: return False @override async def list_collections(self) -> list[str]: """List all available collections.""" try: - endpoint = str(self.config.endpoint).rstrip("/") + endpoint = self.endpoint client = httpx.AsyncClient() try: response = await client.get(f"{endpoint}/v3/collections") @@ -3415,8 +4224,8 @@ class R2RStorage(BaseStorage): } ) return collections - except R2RClientException as e: - raise StorageError(f"Failed to list collections: {e}") from e + except R2RException as exc: + raise StorageError(f"Failed to list collections: {exc}") from exc async def get_document_chunks(self, document_id: str) -> list[dict[str, object]]: """Get all chunks for a specific document.""" @@ -3425,18 +4234,18 @@ class R2RStorage(BaseStorage): return [ dict(_as_mapping(chunk)) for chunk in _as_sequence(getattr(response, "results", ())) ] - except R2RClientException as e: - raise StorageError(f"Failed to get chunks for document {document_id}: {e}") from e + except R2RException as exc: + raise StorageError(f"Failed to get chunks for document {document_id}: {exc}") from exc async def extract_entities(self, document_id: str) -> dict[str, object]: """Extract entities and relationships from a document.""" try: response = await self.client.documents.extract(id=document_id) return dict(_as_mapping(getattr(response, "results", {}))) - except R2RClientException as e: + except R2RException as exc: raise StorageError( - f"Failed to extract entities from document {document_id}: {e}" - ) from e + f"Failed to extract entities from document {document_id}: {exc}" + ) from exc async def get_document_overview(self, document_id: str) -> dict[str, object]: """Get comprehensive document overview and statistics.""" @@ -3453,8 +4262,8 @@ class R2RStorage(BaseStorage): "chunk_count": len(chunk_payload), "chunks": chunk_payload, } - except R2RClientException as e: - raise StorageError(f"Failed to get overview for document {document_id}: {e}") from e + except R2RException as exc: + raise StorageError(f"Failed to get overview for document {document_id}: {exc}") from exc @override async def list_documents( @@ -3482,7 +4291,7 @@ class R2RStorage(BaseStorage): # Get collection ID first collection_id = await self._ensure_collection(collection_name) # Use the collections API to list documents in a specific collection - endpoint = str(self.config.endpoint).rstrip("/") + endpoint = self.endpoint client = httpx.AsyncClient() try: params = {"offset": offset, "limit": limit} @@ -3498,7 +4307,9 @@ class R2RStorage(BaseStorage): else: # List all documents r2r_response = await self.client.documents.list(offset=offset, limit=limit) - documents_data: list[object] | dict[str, object] = getattr(r2r_response, "results", []) + documents_data: list[object] | dict[str, object] = getattr( + r2r_response, "results", [] + ) doc_sequence = _as_sequence( documents_data.get("results", []) @@ -3535,6 +4346,65 @@ class R2RStorage(BaseStorage): raise StorageError(f"Failed to list documents: {e}") from e + +"""Utility modules.""" + +from .metadata_tagger import MetadataTagger +from .vectorizer import Vectorizer + +__all__ = ["MetadataTagger", "Vectorizer"] + + + +"""Main entry point for the ingestion pipeline.""" + +from .cli.main import app + +if __name__ == "__main__": + app() + + + +# API Keys +FIRECRAWL_API_KEY=fc-your-api-key +OPENWEBUI_API_KEY= +WEAVIATE_API_KEY= + +# Endpoints +LLM_ENDPOINT=http://llm.lab +WEAVIATE_ENDPOINT=http://weaviate.yo +OPENWEBUI_ENDPOINT=http://chat.lab + +# Model Configuration +EMBEDDING_MODEL=ollama/bge-m3:latest +EMBEDDING_DIMENSION=1024 + +# Ingestion Settings +BATCH_SIZE=50 +MAX_FILE_SIZE=1000000 +MAX_CRAWL_DEPTH=5 +MAX_CRAWL_PAGES=100 + +# Storage Settings +DEFAULT_STORAGE_BACKEND=weaviate +COLLECTION_PREFIX=docs + +# Prefect Settings +PREFECT_API_URL=http://prefect.lab +PREFECT_API_KEY=0nR4WAkQ3q9MY1bjqATK6pVmolighvrS +PREFECT_WORK_POOL=default + +# Scheduling +DEFAULT_SCHEDULE_INTERVAL=60 + +# Performance +MAX_CONCURRENT_TASKS=5 +REQUEST_TIMEOUT=60 + +# Logging +LOG_LEVEL=INFO + + """Screen components for the TUI application.""" @@ -3773,8 +4643,9 @@ class CollectionOverviewScreen(Screen[None]): """Update the metrics cards display.""" try: dashboard_tab = self.query_one("#dashboard") - metrics_cards = dashboard_tab.query(MetricsCard) - if len(metrics_cards) >= 4: + metrics_cards_query = dashboard_tab.query(MetricsCard) + if len(metrics_cards_query) >= 4: + metrics_cards = list(metrics_cards_query) self._update_card_values(metrics_cards) self._update_status_card(metrics_cards[3]) except NoMatches: @@ -3782,13 +4653,13 @@ class CollectionOverviewScreen(Screen[None]): except Exception as exc: LOGGER.exception("Failed to update dashboard metrics", exc_info=exc) - def _update_card_values(self, metrics_cards: list) -> None: + def _update_card_values(self, metrics_cards: list[MetricsCard]) -> None: """Update individual metric card values.""" metrics_cards[0].query_one(".metrics-value", Static).update(f"{self.total_collections:,}") metrics_cards[1].query_one(".metrics-value", Static).update(f"{self.total_documents:,}") metrics_cards[2].query_one(".metrics-value", Static).update(str(self.active_backends)) - def _update_status_card(self, status_card: object) -> None: + def _update_status_card(self, status_card: MetricsCard) -> None: """Update the system status card.""" if self.active_backends > 0 and self.total_collections > 0: status_text, status_class = "🟢 Healthy", "status-active" @@ -3908,7 +4779,7 @@ class CollectionOverviewScreen(Screen[None]): except Exception as e: status_text.update(f"❌ Error: {e}") - self.notify(f"Failed to refresh: {e}", severity="error") + self.notify(f"Failed to refresh: {e}", severity="error", markup=False) finally: self.is_loading = False loading_indicator.display = False @@ -3923,8 +4794,10 @@ class CollectionOverviewScreen(Screen[None]): collections: list[CollectionInfo] = [] for item in overview: - count_val = int(item.get("count", 0)) - size_mb_val = float(item.get("size_mb", 0.0)) + count_raw = item.get("count", 0) + count_val = int(count_raw) if isinstance(count_raw, (int, str)) else 0 + size_mb_raw = item.get("size_mb", 0.0) + size_mb_val = float(size_mb_raw) if isinstance(size_mb_raw, (int, float, str)) else 0.0 collections.append( CollectionInfo( name=str(item.get("name", "Unknown")), @@ -3939,7 +4812,7 @@ class CollectionOverviewScreen(Screen[None]): return collections except Exception as e: - self.notify(f"Error listing Weaviate collections: {e}", severity="error") + self.notify(f"Error listing Weaviate collections: {e}", severity="error", markup=False) return [] async def list_openwebui_collections(self) -> list[CollectionInfo]: @@ -3954,8 +4827,10 @@ class CollectionOverviewScreen(Screen[None]): collections: list[CollectionInfo] = [] for item in overview: - count_val = int(item.get("count", 0)) - size_mb_val = float(item.get("size_mb", 0.0)) + count_raw = item.get("count", 0) + count_val = int(count_raw) if isinstance(count_raw, (int, str)) else 0 + size_mb_raw = item.get("size_mb", 0.0) + size_mb_val = float(size_mb_raw) if isinstance(size_mb_raw, (int, float, str)) else 0.0 collection_name = str(item.get("name", "Unknown")) collections.append( CollectionInfo( @@ -3971,13 +4846,13 @@ class CollectionOverviewScreen(Screen[None]): return collections except Exception as e: - self.notify(f"Error listing OpenWebUI collections: {e}", severity="error") + self.notify(f"Error listing OpenWebUI collections: {e}", severity="error", markup=False) return [] async def update_collections_table(self) -> None: """Update the collections table with enhanced formatting.""" table = self.query_one("#collections_table", EnhancedDataTable) - table.clear() + table.clear(columns=True) # Add enhanced columns with more metadata table.add_columns("Collection", "Backend", "Documents", "Size", "Type", "Status", "Updated") @@ -4065,9 +4940,7 @@ class CollectionOverviewScreen(Screen[None]): def action_manage(self) -> None: """Manage documents in selected collection.""" if selected := self.get_selected_collection(): - # Get the appropriate storage backend for the collection - storage_backend = self._get_storage_for_collection(selected) - if storage_backend: + if storage_backend := self._get_storage_for_collection(selected): from .documents import DocumentManagementScreen self.app.push_screen(DocumentManagementScreen(selected, storage_backend)) @@ -4246,13 +5119,14 @@ Enjoy the enhanced interface! 🎉 """Dialog screens for confirmations and user interactions.""" +from pathlib import Path from typing import TYPE_CHECKING from textual.app import ComposeResult -from textual.binding import Binding +from textual.binding import Binding, BindingType from textual.containers import Container, Horizontal -from textual.screen import Screen -from textual.widgets import Button, Footer, Header, LoadingIndicator, Static +from textual.screen import ModalScreen, Screen +from textual.widgets import Button, Footer, Header, LoadingIndicator, RichLog, Static from typing_extensions import override from ..models import CollectionInfo @@ -4375,11 +5249,11 @@ class ConfirmDeleteScreen(Screen[None]): return # Refresh parent screen after a short delay to ensure deletion is processed - self.call_later(self.parent_screen.refresh_collections, 0.5) # 500ms delay + self.call_later(lambda _: self.parent_screen.refresh_collections(), 0.5) # 500ms delay self.app.pop_screen() except Exception as e: - self.notify(f"Failed to delete collection: {e}", severity="error") + self.notify(f"Failed to delete collection: {e}", severity="error", markup=False) @@ -4450,9 +5324,12 @@ class ConfirmDocumentDeleteScreen(Screen[None]): loading.display = True try: - if self.parent_screen.weaviate: - # Delete documents - results = await self.parent_screen.weaviate.delete_documents( + if hasattr(self.parent_screen, 'storage') and self.parent_screen.storage: + # Delete documents via storage + # The storage should have delete_documents method for weaviate + storage = self.parent_screen.storage + if hasattr(storage, 'delete_documents'): + results = await storage.delete_documents( self.doc_ids, collection_name=self.collection["name"], ) @@ -4472,9 +5349,96 @@ class ConfirmDocumentDeleteScreen(Screen[None]): self.app.pop_screen() except Exception as e: - self.notify(f"Failed to delete documents: {e}", severity="error") + self.notify(f"Failed to delete documents: {e}", severity="error", markup=False) finally: loading.display = False + + +class LogViewerScreen(ModalScreen[None]): + """Display live log output without disrupting the TUI.""" + + _log_widget: RichLog | None + _log_file: Path | None + + BINDINGS = [ + Binding("escape", "close", "Close"), + Binding("ctrl+l", "close", "Close"), + Binding("s", "show_path", "Log File"), + ] + + def __init__(self) -> None: + super().__init__() + self._log_widget = None + self._log_file = None + + @override + def compose(self) -> ComposeResult: + yield Header(show_clock=True) + yield Container( + Static("📜 Live Application Logs", classes="title"), + Static("Logs update in real time. Press S to reveal the log file path.", classes="subtitle"), + RichLog(id="log_stream", classes="log-stream", wrap=True, highlight=False), + Static("", id="log_file_path", classes="subtitle"), + classes="main_container log-viewer-container", + ) + yield Footer() + + def on_mount(self) -> None: + """Attach this viewer to the parent application once mounted.""" + self._log_widget = self.query_one(RichLog) + from ..app import CollectionManagementApp + + if isinstance(self.app, CollectionManagementApp): + self.app.attach_log_viewer(self) + + def on_unmount(self) -> None: + """Detach from the parent application when closed.""" + from ..app import CollectionManagementApp + + if isinstance(self.app, CollectionManagementApp): + self.app.detach_log_viewer(self) + + def _get_log_widget(self) -> RichLog: + if self._log_widget is None: + self._log_widget = self.query_one(RichLog) + if self._log_widget is None: + raise RuntimeError("RichLog widget not found") + return self._log_widget + + def replace_logs(self, lines: list[str]) -> None: + """Replace rendered logs with the provided history.""" + log_widget = self._get_log_widget() + log_widget.clear() + for line in lines: + log_widget.write(line) + log_widget.scroll_end(animate=False) + + def append_logs(self, lines: list[str]) -> None: + """Append new log lines to the viewer.""" + log_widget = self._get_log_widget() + for line in lines: + log_widget.write(line) + log_widget.scroll_end(animate=False) + + def update_log_file(self, log_file: Path | None) -> None: + """Update the displayed log file path.""" + self._log_file = log_file + label = self.query_one("#log_file_path", Static) + if log_file is None: + label.update("Logs are not currently being persisted to disk.") + else: + label.update(f"Log file: {log_file}") + + def action_close(self) -> None: + """Close the log viewer.""" + self.app.pop_screen() + + def action_show_path(self) -> None: + """Reveal the log file location in a notification.""" + if self._log_file is None: + self.notify("File logging is disabled for this session.", severity="warning") + else: + self.notify(f"Log file available at: {self._log_file}", severity="information", markup=False) @@ -4490,7 +5454,6 @@ from textual.widgets import Button, Footer, Header, Label, LoadingIndicator, Sta from typing_extensions import override from ....storage.base import BaseStorage -from ....storage.weaviate import WeaviateStorage from ..models import CollectionInfo, DocumentInfo from ..widgets import EnhancedDataTable @@ -4595,9 +5558,9 @@ class DocumentManagementScreen(Screen[None]): description=str(doc.get("description", "")), content_type=str(doc.get("content_type", "text/plain")), content_preview=str(doc.get("content_preview", "")), - word_count=int(doc.get("word_count", 0)) - if str(doc.get("word_count", 0)).isdigit() - else 0, + word_count=( + lambda wc_val: int(wc_val) if isinstance(wc_val, (int, str)) and str(wc_val).isdigit() else 0 + )(doc.get("word_count", 0)), timestamp=str(doc.get("timestamp", "")), ) for i, doc in enumerate(raw_docs) @@ -4615,14 +5578,14 @@ class DocumentManagementScreen(Screen[None]): self.update_page_info() except Exception as e: - self.notify(f"Error loading documents: {e}", severity="error") + self.notify(f"Error loading documents: {e}", severity="error", markup=False) finally: loading.display = False async def update_table(self) -> None: """Update the documents table with enhanced metadata display.""" table = self.query_one("#documents_table", EnhancedDataTable) - table.clear() + table.clear(columns=True) # Add enhanced columns with more metadata table.add_columns( @@ -4807,67 +5770,12 @@ class DocumentManagementScreen(Screen[None]): self.action_select_none() - -"""Help screen with keyboard shortcuts and usage information.""" - -from textual.app import ComposeResult -from textual.binding import Binding -from textual.containers import Container, ScrollableContainer -from textual.screen import ModalScreen -from textual.widgets import Button, Markdown, Rule, Static -from typing_extensions import override - - -class HelpScreen(ModalScreen[None]): - """Modern help screen with comprehensive keyboard shortcuts.""" - - help_content: str - - BINDINGS = [ - Binding("escape", "app.pop_screen", "Close"), - Binding("q", "app.pop_screen", "Close"), - Binding("enter", "app.pop_screen", "Close"), - Binding("f1", "app.pop_screen", "Close"), - ] - - def __init__(self, help_content: str): - super().__init__() - self.help_content = help_content - - @override - def compose(self) -> ComposeResult: - with Container(classes="modal-container"): - yield Static("📚 Help & Keyboard Shortcuts", classes="title") - yield Static("Enhanced navigation and productivity features", classes="subtitle") - yield Rule(line_style="heavy") - - with ScrollableContainer(): - yield Markdown(self.help_content) - - yield Container( - Button("✅ Got it! (Press Escape or Enter)", id="close_btn", variant="primary"), - classes="action_buttons center", - ) - - def on_mount(self) -> None: - """Initialize the help screen.""" - # Focus the close button - self.query_one("#close_btn").focus() - - def on_button_pressed(self, event: Button.Pressed) -> None: - """Close help screen.""" - if event.button.id == "close_btn": - self.app.pop_screen() - - """Enhanced ingestion screen with multi-storage support.""" from __future__ import annotations -import asyncio -from datetime import datetime -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING from textual import work from textual.app import ComposeResult @@ -4877,9 +5785,9 @@ from textual.screen import ModalScreen from textual.widgets import Button, Checkbox, Input, Label, LoadingIndicator, Rule, Static from typing_extensions import override -from ....core.models import IngestionJob, IngestionSource, StorageBackend -from ..models import CollectionInfo +from ....core.models import IngestionResult, IngestionSource, StorageBackend from ....flows.ingestion import create_ingestion_flow +from ..models import CollectionInfo from ..utils.storage_manager import StorageManager from ..widgets import EnhancedProgressBar @@ -4975,7 +5883,7 @@ class IngestionScreen(ModalScreen[None]): classes="type_buttons", ), Rule(line_style="dashed"), - Label("🗄️ Target Storages:", classes="input-label", id="backend_label"), + Label(f"🗄️ Target Storages ({len(self.available_backends)} available):", classes="input-label", id="backend_label"), Container( *self._create_backend_checkbox_widgets(), classes="backend-selection", @@ -4985,6 +5893,7 @@ class IngestionScreen(ModalScreen[None]): Button("Clear Selection", id="clear_backends", variant="default"), classes="backend-actions", ), + Static("📋 Selected: None", id="selection_status", classes="selection-status"), classes="input-section card", ) @@ -5022,6 +5931,7 @@ class IngestionScreen(ModalScreen[None]): self.query_one("#loading").display = False self.query_one("#url_input", Input).focus() self._set_backend_selection(self.selected_backends) + self._update_selection_status() def action_select_web(self) -> None: self.selected_type = IngestionSource.WEB @@ -5054,13 +5964,22 @@ class IngestionScreen(ModalScreen[None]): self.action_select_docs() elif button_id == "select_all_backends": self._set_backend_selection(self.available_backends) + self._update_selection_status() elif button_id == "clear_backends": self._set_backend_selection([]) + self._update_selection_status() elif button_id == "start_btn": self.action_start_ingestion() elif button_id == "cancel_btn": self.app.pop_screen() + def on_checkbox_changed(self, event: Checkbox.Changed) -> None: + """Handle checkbox state changes for backend selection.""" + if event.checkbox.id and event.checkbox.id.startswith("backend_"): + # Update the selected backends list based on current checkbox states + self.selected_backends = self._resolve_selected_backends() + self._update_selection_status() + def on_input_submitted(self, event: Input.Submitted) -> None: if event.input.id in ("url_input", "collection_input"): self.action_start_ingestion() @@ -5122,14 +6041,10 @@ class IngestionScreen(ModalScreen[None]): checkbox = self.query_one(checkbox_id, Checkbox) if checkbox.value: selected.append(backend) - if not selected and self.available_backends: - selected.append(self.available_backends[0]) return selected def _set_backend_selection(self, backends: list[StorageBackend]) -> None: normalized = [backend for backend in BACKEND_ORDER if backend in backends] - if not normalized and self.available_backends: - normalized = [self.available_backends[0]] for backend in BACKEND_ORDER: if backend not in self.available_backends: continue @@ -5138,6 +6053,33 @@ class IngestionScreen(ModalScreen[None]): checkbox.value = backend in normalized self.selected_backends = normalized + def _update_selection_status(self) -> None: + """Update the visual indicator showing current storage selection.""" + try: + status_widget = self.query_one("#selection_status", Static) + + if not self.selected_backends: + status_widget.update("📋 Selected: None") + elif len(self.selected_backends) == 1: + backend_name = BACKEND_LABELS.get(self.selected_backends[0], self.selected_backends[0].value) + status_widget.update(f"📋 Selected: {backend_name}") + else: + # Multiple backends selected + backend_names = [ + BACKEND_LABELS.get(backend, backend.value) + for backend in self.selected_backends + ] + if len(backend_names) <= 3: + # Show all names if 3 or fewer + names_str = ", ".join(backend_names) + status_widget.update(f"📋 Selected: {names_str}") + else: + # Show count if more than 3 + status_widget.update(f"📋 Selected: {len(self.selected_backends)} backends") + except Exception: + # Widget might not exist yet during initialization + pass + def _derive_initial_backends(self) -> list[StorageBackend]: backend_info = self.collection.get("backend", "") @@ -5164,21 +6106,40 @@ class IngestionScreen(ModalScreen[None]): return [backend] return [self.available_backends[0]] - @work(exclusive=True) - async def perform_ingestion(self, source_url: str, collection_name: str = "") -> None: - loading = self.query_one("#loading") - progress = self.query_one("#enhanced_progress", EnhancedProgressBar) - progress_text = self.query_one("#progress_text", Static) + @work(exclusive=True, thread=True) + def perform_ingestion(self, source_url: str, collection_name: str = "") -> None: + import asyncio + from typing import cast backends = self._resolve_selected_backends() self.selected_backends = backends - try: - loading.display = True + def update_ui(action: str) -> None: + def _update() -> None: + try: + loading = self.query_one("#loading") + if action == "show_loading": + loading.display = True + elif action == "hide_loading": + loading.display = False + except Exception: + pass + cast("CollectionManagementApp", self.app).call_from_thread(_update) - progress.update_progress(5, "Initializing Prefect flows...") - progress_text.update(f"🚀 Starting {len(backends)} Prefect flow(s)...") - await asyncio.sleep(0.2) + def progress_reporter(percent: int, message: str) -> None: + def _update_progress() -> None: + try: + progress = self.query_one("#enhanced_progress", EnhancedProgressBar) + progress_text = self.query_one("#progress_text", Static) + progress.update_progress(percent, message) + progress_text.update(message) + except Exception: + pass + cast("CollectionManagementApp", self.app).call_from_thread(_update_progress) + + try: + update_ui("show_loading") + progress_reporter(5, "🚀 Starting Prefect flows...") # Use user-provided collection name or fall back to default final_collection_name = collection_name or self.collection.get("name") @@ -5189,18 +6150,25 @@ class IngestionScreen(ModalScreen[None]): for i, backend in enumerate(backends): progress_percent = 20 + (60 * i) // len(backends) - progress.update_progress(progress_percent, f"Running flow for {backend.value}...") - progress_text.update(f"🔗 Processing {backend.value} backend ({i+1}/{len(backends)})...") - await asyncio.sleep(0.2) + progress_reporter(progress_percent, f"🔗 Processing {backend.value} backend ({i+1}/{len(backends)})...") try: - # Run the Prefect flow for this backend - result = await create_ingestion_flow( - source_url=source_url, - source_type=self.selected_type, - storage_backend=backend, - collection_name=final_collection_name, - ) + # Run the Prefect flow for this backend using asyncio.run with timeout + import asyncio + + async def run_flow_with_timeout(current_backend: object = backend) -> IngestionResult: + return await asyncio.wait_for( + create_ingestion_flow( + source_url=source_url, + source_type=self.selected_type, + storage_backend=current_backend, + collection_name=final_collection_name, + progress_callback=progress_reporter, + ), + timeout=600.0 # 10 minute timeout + ) + + result = asyncio.run(run_flow_with_timeout()) total_successful += result.documents_processed total_failed += result.documents_failed @@ -5208,40 +6176,64 @@ class IngestionScreen(ModalScreen[None]): if result.error_messages: flow_errors.extend([f"{backend.value}: {err}" for err in result.error_messages]) + except TimeoutError: + error_msg = f"{backend.value}: Timeout after 10 minutes" + flow_errors.append(error_msg) + progress_reporter(0, f"❌ {backend.value} timed out") + def notify_timeout(msg: str = f"⏰ {backend.value} flow timed out after 10 minutes") -> None: + try: + self.notify(msg, severity="error", markup=False) + except Exception: + pass + cast("CollectionManagementApp", self.app).call_from_thread(notify_timeout) except Exception as exc: flow_errors.append(f"{backend.value}: {exc}") - self.notify(f"❌ {backend.value} flow failed: {exc}", severity="error") + def notify_error(msg: str = f"❌ {backend.value} flow failed: {exc}") -> None: + try: + self.notify(msg, severity="error", markup=False) + except Exception: + pass + cast("CollectionManagementApp", self.app).call_from_thread(notify_error) successful = total_successful failed = total_failed - progress.update_progress(100, "Completed successfully!") - progress_text.update( - f"🎉 Completed {len(backends)} Prefect flow(s)!" - ) + progress_reporter(100, "🎉 Completed successfully!") - if successful > 0: - self.notify( - f"🎉 Successfully ingested {successful} documents across {len(backends)} backend(s) via Prefect!", - severity="information", - ) - if failed > 0: - self.notify(f"⚠️ {failed} documents failed to process", severity="warning") + def notify_results() -> None: + try: + if successful > 0: + self.notify( + f"🎉 Successfully ingested {successful} documents across {len(backends)} backend(s) via Prefect!", + severity="information", + ) + if failed > 0: + self.notify(f"⚠️ {failed} documents failed to process", severity="warning") - if flow_errors: - for error in flow_errors: - self.notify(f"⚠️ {error}", severity="warning") + if flow_errors: + for error in flow_errors: + self.notify(f"⚠️ {error}", severity="warning", markup=False) + except Exception: + pass - await asyncio.sleep(2) + cast("CollectionManagementApp", self.app).call_from_thread(notify_results) + + import time + time.sleep(2) cast("CollectionManagementApp", self.app).pop_screen() except Exception as exc: # pragma: no cover - defensive - progress.update_progress(0, "Prefect flows failed") - progress_text.update(f"❌ Prefect flows error: {exc}") - self.notify(f"❌ Prefect flows failed: {exc}", severity="error") - await asyncio.sleep(2) + progress_reporter(0, f"❌ Prefect flows error: {exc}") + def notify_error(msg: str = f"❌ Prefect flows failed: {exc}") -> None: + try: + self.notify(msg, severity="error") + except Exception: + pass + cast("CollectionManagementApp", self.app).call_from_thread(notify_error) + import time + time.sleep(2) finally: - loading.display = False + update_ui("hide_loading") @@ -5359,7 +6351,7 @@ class SearchScreen(Screen[None]): async def search_collection(self, query: str) -> None: """Search the collection.""" - loading = self.query_one("#loading") + loading = self.query_one("#loading", LoadingIndicator) table = self.query_one("#results_table", EnhancedDataTable) status = self.query_one("#search_status", Static) @@ -5370,11 +6362,11 @@ class SearchScreen(Screen[None]): self._update_search_status(status, query, results, table) except Exception as e: status.update(f"Search error: {e}") - self.notify(f"Search error: {e}", severity="error") + self.notify(f"Search error: {e}", severity="error", markup=False) finally: loading.display = False - def _setup_search_ui(self, loading, table, status, query: str) -> None: + def _setup_search_ui(self, loading: LoadingIndicator, table: EnhancedDataTable, status: Static, query: str) -> None: """Setup the search UI elements.""" loading.display = True status.update(f"🔍 Searching for '{query}'...") @@ -5389,7 +6381,7 @@ class SearchScreen(Screen[None]): return await self.search_openwebui(query) return [] - def _populate_results_table(self, table, results: list[dict[str, str | float]]) -> None: + def _populate_results_table(self, table: EnhancedDataTable, results: list[dict[str, str | float]]) -> None: """Populate the results table with search results.""" for result in results: row_data = self._format_result_row(result) @@ -5430,7 +6422,7 @@ class SearchScreen(Screen[None]): content = str(content) if content is not None else "" return f"{content[:60]}..." if len(content) > 60 else content - def _format_score(self, score) -> str: + def _format_score(self, score: object) -> str: """Format search score for display.""" if isinstance(score, (int, float)): return f"{score:.3f}" @@ -5440,7 +6432,7 @@ class SearchScreen(Screen[None]): return str(score) def _update_search_status( - self, status, query: str, results: list[dict[str, str | float]], table + self, status: Static, query: str, results: list[dict[str, str | float]], table: EnhancedDataTable ) -> None: """Update search status and notifications based on results.""" if not results: @@ -5492,7 +6484,7 @@ class SearchScreen(Screen[None]): ) return formatted_results except Exception as e: - self.notify(f"Weaviate search error: {e}", severity="error") + self.notify(f"Weaviate search error: {e}", severity="error", markup=False) return [] async def search_openwebui(self, query: str) -> list[dict[str, str | float]]: @@ -5506,35 +6498,98 @@ class SearchScreen(Screen[None]): self.notify("OpenWebUI search not yet implemented", severity="warning") return [] except Exception as e: - self.notify(f"OpenWebUI search error: {e}", severity="error") + self.notify(f"OpenWebUI search error: {e}", severity="error", markup=False) return [] - -"""Utility functions for the TUI.""" - -from .runners import dashboard, run_textual_tui - -__all__ = ["dashboard", "run_textual_tui"] - - """TUI runner functions and initialization.""" +from __future__ import annotations + import asyncio +import logging +from logging import Logger +from logging.handlers import QueueHandler, RotatingFileHandler +from pathlib import Path +from queue import Queue +from typing import NamedTuple from ....config import configure_prefect, get_settings from ....core.models import StorageBackend from .storage_manager import StorageManager +class _TuiLoggingContext(NamedTuple): + """Container describing configured logging outputs for the TUI.""" + + queue: Queue[logging.LogRecord] + formatter: logging.Formatter + log_file: Path | None + + +_logging_context: _TuiLoggingContext | None = None + + +def _configure_tui_logging(*, log_level: str) -> _TuiLoggingContext: + """Configure logging so that messages do not break the TUI output.""" + + global _logging_context + if _logging_context is not None: + return _logging_context + + resolved_level = getattr(logging, log_level.upper(), logging.INFO) + log_queue: Queue[logging.LogRecord] = Queue() + formatter = logging.Formatter( + fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + root_logger = logging.getLogger() + root_logger.setLevel(resolved_level) + + # Remove existing stream handlers to prevent console flicker inside the TUI + for handler in list(root_logger.handlers): + root_logger.removeHandler(handler) + + queue_handler = QueueHandler(log_queue) + queue_handler.setLevel(resolved_level) + root_logger.addHandler(queue_handler) + + log_file: Path | None = None + try: + log_dir = Path.cwd() / "logs" + log_dir.mkdir(parents=True, exist_ok=True) + log_file = log_dir / "tui.log" + file_handler = RotatingFileHandler( + log_file, + maxBytes=2_000_000, + backupCount=5, + encoding="utf-8", + ) + file_handler.setLevel(resolved_level) + file_handler.setFormatter(formatter) + root_logger.addHandler(file_handler) + except OSError as exc: # pragma: no cover - filesystem specific + fallback = logging.getLogger(__name__) + fallback.warning("Failed to configure file logging for TUI: %s", exc) + + _logging_context = _TuiLoggingContext(log_queue, formatter, log_file) + return _logging_context + + +LOGGER: Logger = logging.getLogger(__name__) + + async def run_textual_tui() -> None: """Run the enhanced modern TUI with better error handling and initialization.""" settings = get_settings() configure_prefect(settings) - print("🚀 Initializing Modern Collection Management System...") - print("🔄 Scanning available storage backends...") + logging_context = _configure_tui_logging(log_level=settings.log_level) + + LOGGER.info("Initializing collection management TUI") + LOGGER.info("Scanning available storage backends") # Initialize storage manager storage_manager = StorageManager(settings) @@ -5543,22 +6598,22 @@ async def run_textual_tui() -> None: # Report initialization results for backend, success in backend_status.items(): if success: - print(f"✅ {backend.value} connected successfully!") + LOGGER.info("%s connected successfully", backend.value) else: - print(f"⚠️ {backend.value} connection failed") + LOGGER.warning("%s connection failed", backend.value) available_backends = storage_manager.get_available_backends() if not available_backends: - print("❌ Error: Could not connect to any storage backend") - print("Please check your configuration and try again.") - print("\nSupported backends:") - print(" - Weaviate (vector database)") - print(" - OpenWebUI (knowledge base)") - print(" - R2R (retrieval-augmented generation)") + LOGGER.error("Could not connect to any storage backend") + LOGGER.info("Please check your configuration and try again") + LOGGER.info("Supported backends: Weaviate, OpenWebUI, R2R") return - print(f"🎉 Launching Enhanced TUI with {len(available_backends)} backend(s)...") - print(f"Available: {', '.join(b.value for b in available_backends)}") + LOGGER.info( + "Launching TUI with %d backend(s): %s", + len(available_backends), + ", ".join(backend.value for backend in available_backends), + ) # Get individual storage instances for backward compatibility weaviate = storage_manager.get_backend(StorageBackend.WEAVIATE) @@ -5567,13 +6622,21 @@ async def run_textual_tui() -> None: # Import here to avoid circular import from ..app import CollectionManagementApp - app = CollectionManagementApp(storage_manager, weaviate, openwebui, r2r) + app = CollectionManagementApp( + storage_manager, + weaviate, + openwebui, + r2r, + log_queue=logging_context.queue, + log_formatter=logging_context.formatter, + log_file=logging_context.log_file, + ) try: await app.run_async() finally: - print("🔄 Shutting down storage connections...") + LOGGER.info("Shutting down storage connections") await storage_manager.close_all() - print("🔌 All storage connections closed gracefully.") + LOGGER.info("All storage connections closed gracefully") def dashboard() -> None: @@ -5581,52 +6644,6 @@ def dashboard() -> None: asyncio.run(run_textual_tui()) - -"""Enhanced widgets with keyboard navigation support.""" - -from .cards import MetricsCard -from .indicators import EnhancedProgressBar, StatusIndicator -from .tables import EnhancedDataTable - -__all__ = [ - "MetricsCard", - "StatusIndicator", - "EnhancedProgressBar", - "EnhancedDataTable", -] - - - -"""Metrics card widget.""" - -from typing import Any - -from textual.app import ComposeResult -from textual.widgets import Static -from typing_extensions import override - - -class MetricsCard(Static): - """A modern metrics display card.""" - - title: str - value: str - description: str - - def __init__(self, title: str, value: str, description: str = "", **kwargs: Any) -> None: - super().__init__(**kwargs) - self.title = title - self.value = value - self.description = description - - @override - def compose(self) -> ComposeResult: - yield Static(self.value, classes="metrics-value") - yield Static(self.title, classes="metrics-label") - if self.description: - yield Static(self.description, classes="metrics-description") - - """Status indicators and progress bars with enhanced visual feedback.""" @@ -5654,19 +6671,22 @@ class StatusIndicator(Static): # Remove previous status classes self.remove_class("status-active", "status-error", "status-warning", "pulse", "glow") - if status.lower() in {"active", "online", "connected", "✓ active"}: + status_lower = status.lower() + + if (status_lower in {"active", "online", "connected", "✓ active"} or + status_lower.endswith("active") or "✓" in status_lower and "active" in status_lower): self.add_class("status-active") self.add_class("glow") self.update(f"🟢 {status}") - elif status.lower() in {"error", "failed", "offline", "disconnected"}: + elif status_lower in {"error", "failed", "offline", "disconnected"}: self.add_class("status-error") self.add_class("pulse") self.update(f"🔴 {status}") - elif status.lower() in {"warning", "pending", "in_progress"}: + elif status_lower in {"warning", "pending", "in_progress"}: self.add_class("status-warning") self.add_class("pulse") self.update(f"🟡 {status}") - elif status.lower() in {"loading", "connecting"}: + elif status_lower in {"loading", "connecting"}: self.add_class("shimmer") self.update(f"🔄 {status}") else: @@ -5716,172 +6736,36 @@ class EnhancedProgressBar(Static): status_display.update(f"🚀 {self.status_text}") - -"""Enhanced DataTable with improved keyboard navigation.""" - -from typing import Any - -from textual import events -from textual.binding import Binding -from textual.message import Message -from textual.widgets import DataTable - - -class EnhancedDataTable(DataTable[Any]): - """DataTable with enhanced keyboard navigation and visual feedback.""" - - BINDINGS = [ - Binding("up,k", "cursor_up", "Cursor Up", show=False), - Binding("down,j", "cursor_down", "Cursor Down", show=False), - Binding("left,h", "cursor_left", "Cursor Left", show=False), - Binding("right,l", "cursor_right", "Cursor Right", show=False), - Binding("home", "cursor_home", "First Row", show=False), - Binding("end", "cursor_end", "Last Row", show=False), - Binding("pageup", "page_up", "Page Up", show=False), - Binding("pagedown", "page_down", "Page Down", show=False), - Binding("enter", "select_cursor", "Select", show=False), - Binding("space", "toggle_selection", "Toggle Selection", show=False), - Binding("ctrl+a", "select_all", "Select All", show=False), - Binding("ctrl+shift+a", "clear_selection", "Clear Selection", show=False), - ] - - def __init__(self, **kwargs: Any) -> None: - super().__init__(**kwargs) - self.cursor_type = "row" # Default to row selection - self.zebra_stripes = True # Enable zebra striping for better visibility - self.show_cursor = True - - def on_key(self, event: events.Key) -> None: - """Handle additional keyboard shortcuts.""" - if event.key == "ctrl+1": - # Jump to first column - self.move_cursor(column=0) - event.prevent_default() - elif event.key == "ctrl+9": - # Jump to last column - if self.columns: - self.move_cursor(column=len(self.columns) - 1) - event.prevent_default() - elif event.key == "/": - # Start quick search (to be implemented by parent) - self.post_message(self.QuickSearch(self)) - event.prevent_default() - elif event.key == "escape": - # Clear selection or exit search - # Clear selection by calling action - self.action_clear_selection() - event.prevent_default() - # No else clause needed - just handle our events - - def action_cursor_home(self) -> None: - """Move cursor to first row.""" - if self.row_count > 0: - self.move_cursor(row=0) - - def action_cursor_end(self) -> None: - """Move cursor to last row.""" - if self.row_count > 0: - self.move_cursor(row=self.row_count - 1) - - def action_page_up(self) -> None: - """Move cursor up by visible page size.""" - if self.row_count > 0: - page_size = max(1, self.size.height // 2) # Approximate visible rows - new_row = max(0, self.cursor_coordinate.row - page_size) - self.move_cursor(row=new_row) - - def action_page_down(self) -> None: - """Move cursor down by visible page size.""" - if self.row_count > 0: - page_size = max(1, self.size.height // 2) # Approximate visible rows - new_row = min(self.row_count - 1, self.cursor_coordinate.row + page_size) - self.move_cursor(row=new_row) - - def action_toggle_selection(self) -> None: - """Toggle selection of current row.""" - if self.row_count > 0: - current_row = self.cursor_coordinate.row - # This will be handled by the parent screen - self.post_message(self.RowToggled(self, current_row)) - - def action_select_all(self) -> None: - """Select all rows.""" - # This will be handled by the parent screen - self.post_message(self.SelectAll(self)) - - def action_clear_selection(self) -> None: - """Clear all selections.""" - # This will be handled by the parent screen - self.post_message(self.ClearSelection(self)) - - # Custom messages for enhanced functionality - class QuickSearch(Message): - """Posted when user wants to start a quick search.""" - - def __init__(self, table: "EnhancedDataTable") -> None: - super().__init__() - self.table = table - - class RowToggled(Message): - """Posted when a row selection is toggled.""" - - def __init__(self, table: "EnhancedDataTable", row_index: int) -> None: - super().__init__() - self.table = table - self.row_index = row_index - - class SelectAll(Message): - """Posted when user wants to select all rows.""" - - def __init__(self, table: "EnhancedDataTable") -> None: - super().__init__() - self.table = table - - class ClearSelection(Message): - """Posted when user wants to clear selection.""" - - def __init__(self, table: "EnhancedDataTable") -> None: - super().__init__() - self.table = table - - - -"""Enhanced TUI package with keyboard navigation and modular architecture.""" - -from .app import CollectionManagementApp -from .models import CollectionInfo, DocumentInfo -from .utils import dashboard, run_textual_tui - -__all__ = [ - "CollectionManagementApp", - "CollectionInfo", - "DocumentInfo", - "dashboard", - "run_textual_tui", -] - - """Main TUI application with enhanced keyboard navigation.""" from __future__ import annotations +import logging import os -from typing import TYPE_CHECKING +from collections import deque +from pathlib import Path +from queue import Empty, Queue +from typing import TYPE_CHECKING, ClassVar, Literal from textual import events from textual.app import App -from textual.binding import Binding +from textual.binding import Binding, BindingType +from textual.timer import Timer from ...storage.base import BaseStorage from ...storage.openwebui import OpenWebUIStorage from ...storage.weaviate import WeaviateStorage -from .screens import CollectionOverviewScreen, HelpScreen +from .screens.dashboard import CollectionOverviewScreen +from .screens.help import HelpScreen from .styles import TUI_CSS from .utils.storage_manager import StorageManager if TYPE_CHECKING: + from logging import Formatter, LogRecord + from ...storage.r2r.storage import R2RStorage + from .screens.dialogs import LogViewerScreen else: # pragma: no cover - optional dependency fallback R2RStorage = BaseStorage @@ -5890,9 +6774,20 @@ else: # pragma: no cover - optional dependency fallback class CollectionManagementApp(App[None]): """Enhanced modern Textual application with comprehensive keyboard navigation.""" - CSS = TUI_CSS + CSS: ClassVar[str] = TUI_CSS + TITLE = "Collection Management" + SUB_TITLE = "Document Ingestion Pipeline" - BINDINGS = [ + def safe_notify( + self, + message: str, + *, + severity: Literal["information", "warning", "error"] = "information", + ) -> None: + """Safely notify with markup disabled to prevent parsing errors.""" + self.notify(message, severity=severity, markup=False) + + BINDINGS: ClassVar[list[BindingType]] = [ Binding("q", "quit", "Quit"), Binding("ctrl+c", "quit", "Quit"), Binding("ctrl+q", "quit", "Quit"), @@ -5902,6 +6797,7 @@ class CollectionManagementApp(App[None]): # Global navigation shortcuts Binding("ctrl+r", "refresh_current", "Refresh Current Screen"), Binding("ctrl+w", "close_current", "Close Current Screen"), + Binding("ctrl+l", "toggle_logs", "Logs"), # Tab navigation shortcuts Binding("ctrl+1", "dashboard_tab", "Dashboard", show=False), Binding("ctrl+2", "collections_tab", "Collections", show=False), @@ -5912,6 +6808,12 @@ class CollectionManagementApp(App[None]): weaviate: WeaviateStorage | None openwebui: OpenWebUIStorage | None r2r: R2RStorage | BaseStorage | None + log_queue: Queue[LogRecord] | None + _log_formatter: Formatter + _log_buffer: deque[str] + _log_viewer: LogViewerScreen | None + _log_file: Path | None + _log_timer: Timer | None def __init__( self, @@ -5919,12 +6821,27 @@ class CollectionManagementApp(App[None]): weaviate: WeaviateStorage | None = None, openwebui: OpenWebUIStorage | None = None, r2r: R2RStorage | BaseStorage | None = None, + *, + log_queue: Queue[LogRecord] | None = None, + log_formatter: Formatter | None = None, + log_file: Path | None = None, ) -> None: super().__init__() self.storage_manager = storage_manager self.weaviate = weaviate self.openwebui = openwebui self.r2r = r2r + # Remove direct assignment to read-only title properties + # These should be set through class attributes or overridden methods + self.log_queue = log_queue + self._log_formatter = log_formatter or logging.Formatter( + fmt="%(asctime)s | %(levelname)s | %(name)s | %(message)s", + datefmt="%H:%M:%S", + ) + self._log_buffer = deque(maxlen=500) + self._log_viewer = None + self._log_file = log_file + self._log_timer = None def on_mount(self) -> None: """Initialize the enhanced app with better branding.""" @@ -5949,6 +6866,54 @@ class CollectionManagementApp(App[None]): self.r2r, ) ) + if self.log_queue is not None and self._log_timer is None: + # Poll the queue so log output is captured without blocking the UI loop + self._log_timer = self.set_interval(0.25, self._drain_log_queue) + + def _drain_log_queue(self) -> None: + """Drain queued log records and route them to the active log viewer.""" + if self.log_queue is None: + return + + drained: list[str] = [] + while True: + try: + record = self.log_queue.get_nowait() + except Empty: + break + message = self._log_formatter.format(record) + self._log_buffer.append(message) + drained.append(message) + + if drained and self._log_viewer is not None: + self._log_viewer.append_logs(drained) + + def attach_log_viewer(self, viewer: LogViewerScreen) -> None: + """Register an active log viewer and hydrate it with existing entries.""" + self._log_viewer = viewer + viewer.replace_logs(list(self._log_buffer)) + viewer.update_log_file(self._log_file) + # Drain once more to deliver any entries gathered between instantiation and mount + self._drain_log_queue() + + def detach_log_viewer(self, viewer: LogViewerScreen) -> None: + """Remove the current log viewer when it is dismissed.""" + if self._log_viewer is viewer: + self._log_viewer = None + + def get_log_file_path(self) -> Path | None: + """Return the active log file path if configured.""" + return self._log_file + + def action_toggle_logs(self) -> None: + """Toggle the log viewer modal screen.""" + if self._log_viewer is not None: + _ = self.pop_screen() + return + + from .screens.dialogs import LogViewerScreen # Local import to avoid cycle + + _ = self.push_screen(LogViewerScreen()) def action_help(self) -> None: """Show comprehensive help information with all keyboard shortcuts.""" @@ -6039,35 +7004,39 @@ class CollectionManagementApp(App[None]): def action_refresh_current(self) -> None: """Refresh the current screen if it supports it.""" current_screen = self.screen - if hasattr(current_screen, "action_refresh"): - current_screen.action_refresh() # type: ignore[attr-defined] - else: - self.notify("Current screen doesn't support refresh", severity="information") + handler = getattr(current_screen, "action_refresh", None) + if callable(handler): + _ = handler() + return + self.notify("Current screen doesn't support refresh", severity="information") def action_close_current(self) -> None: """Close current screen/dialog.""" if len(self.screen_stack) > 1: # Don't close the main screen _ = self.pop_screen() else: - _ = self.notify("Cannot close main screen. Use Q to quit.", severity="warning") + self.notify("Cannot close main screen. Use Q to quit.", severity="warning") def action_dashboard_tab(self) -> None: """Switch to dashboard tab in current screen.""" current_screen = self.screen - if hasattr(current_screen, "action_tab_dashboard"): - current_screen.action_tab_dashboard() # type: ignore[attr-defined] + handler = getattr(current_screen, "action_tab_dashboard", None) + if callable(handler): + _ = handler() def action_collections_tab(self) -> None: """Switch to collections tab in current screen.""" current_screen = self.screen - if hasattr(current_screen, "action_tab_collections"): - current_screen.action_tab_collections() # type: ignore[attr-defined] + handler = getattr(current_screen, "action_tab_collections", None) + if callable(handler): + _ = handler() def action_analytics_tab(self) -> None: """Switch to analytics tab in current screen.""" current_screen = self.screen - if hasattr(current_screen, "action_tab_analytics"): - current_screen.action_tab_analytics() # type: ignore[attr-defined] + handler = getattr(current_screen, "action_tab_analytics", None) + if callable(handler): + _ = handler() def on_key(self, event: events.Key) -> None: """Handle global keyboard shortcuts.""" @@ -6078,7 +7047,7 @@ class CollectionManagementApp(App[None]): _ = event.prevent_default() elif event.key == "ctrl+alt+r": # Force refresh all connections - _ = self.notify("🔄 Refreshing all connections...", severity="information") + self.notify("🔄 Refreshing all connections...", severity="information") # This could trigger a full reinit if needed _ = event.prevent_default() # No else clause needed - just handle our events @@ -7313,7 +8282,7 @@ def get_css_for_theme(theme_type: ThemeType) -> str: return css -def apply_theme_to_app(app, theme_type: ThemeType) -> None: +def apply_theme_to_app(app: object, theme_type: ThemeType) -> None: """Apply a theme to a Textual app instance.""" try: css = set_theme(theme_type) @@ -7322,7 +8291,7 @@ def apply_theme_to_app(app, theme_type: ThemeType) -> None: app.stylesheet.parse(css) elif hasattr(app, "CSS"): app.CSS = css - else: + elif hasattr(app, "refresh"): # Fallback: try to refresh the app with new CSS app.refresh() except Exception as e: @@ -7334,7 +8303,7 @@ def apply_theme_to_app(app, theme_type: ThemeType) -> None: class ThemeSwitcher: """Helper class for managing theme switching in TUI applications.""" - def __init__(self, app=None): + def __init__(self, app: object | None = None) -> None: self.app = app self.theme_history = [ThemeType.DARK] @@ -7922,14 +8891,6 @@ def apply_responsive_theme() -> str: return f"{custom_properties}\n{base_css}\n{responsive_css}" - -"""CLI module for the ingestion pipeline.""" - -from .main import app - -__all__ = ["app"] - - """CLI interface for ingestion pipeline.""" @@ -7943,7 +8904,13 @@ from rich.progress import BarColumn, Progress, SpinnerColumn, TaskProgressColumn from rich.table import Table from ..config import configure_prefect, get_settings -from ..core.models import IngestionResult, IngestionSource, StorageBackend +from ..core.models import ( + IngestionResult, + IngestionSource, + StorageBackend, + StorageConfig, +) +from pydantic import SecretStr from ..flows.ingestion import create_ingestion_flow from ..flows.scheduler import create_scheduled_deployment, serve_deployments @@ -8058,7 +9025,22 @@ def ingest( progress.update(task, advance=50, description="✅ Ingestion complete!") return result - result = asyncio.run(run_with_progress()) + # Use asyncio.run() with proper event loop handling + try: + result = asyncio.run(run_with_progress()) + except RuntimeError as e: + if "asyncio.run() cannot be called from a running event loop" in str(e): + # If we're already in an event loop (e.g., in Jupyter), use nest_asyncio + try: + import nest_asyncio + nest_asyncio.apply() + result = asyncio.run(run_with_progress()) + except ImportError: + # Fallback: get the current loop and run the coroutine + loop = asyncio.get_event_loop() + result = loop.run_until_complete(run_with_progress()) + else: + raise # Enhanced results display status_color = "green" if result.status.value == "completed" else "red" @@ -8357,6 +9339,22 @@ def search( asyncio.run(run_search(query, collection, backend.value, limit)) +@app.command(name="blocks") +def blocks_command() -> None: + """🧩 List and manage Prefect Blocks.""" + console.print("[bold cyan]📦 Prefect Blocks Management[/bold cyan]") + console.print("Use 'prefect block register --module ingest_pipeline.core.models' to register custom blocks") + console.print("Use 'prefect block ls' to list available blocks") + + +@app.command(name="variables") +def variables_command() -> None: + """📊 Manage Prefect Variables.""" + console.print("[bold cyan]📊 Prefect Variables Management[/bold cyan]") + console.print("Use 'prefect variable set VARIABLE_NAME value' to set variables") + console.print("Use 'prefect variable ls' to list variables") + + async def run_ingestion( url: str, source_type: IngestionSource, @@ -8388,8 +9386,8 @@ async def run_list_collections() -> None: """ List collections across storage backends. """ - from ..config import configure_prefect, get_settings - from ..core.models import StorageBackend, StorageConfig + from ..config import get_settings + from ..core.models import StorageBackend from ..storage.openwebui import OpenWebUIStorage from ..storage.weaviate import WeaviateStorage @@ -8403,7 +9401,7 @@ async def run_list_collections() -> None: weaviate_config = StorageConfig( backend=StorageBackend.WEAVIATE, endpoint=settings.weaviate_endpoint, - api_key=settings.weaviate_api_key, + api_key=SecretStr(settings.weaviate_api_key) if settings.weaviate_api_key else None, collection_name="default", ) weaviate = WeaviateStorage(weaviate_config) @@ -8412,7 +9410,8 @@ async def run_list_collections() -> None: overview = await weaviate.describe_collections() for item in overview: name = str(item.get("name", "Unknown")) - count = int(item.get("count", 0)) + count_val = item.get("count", 0) + count = int(count_val) if isinstance(count_val, (int, str)) else 0 weaviate_collections.append((name, count)) except Exception as e: console.print(f"❌ [red]Weaviate connection failed: {e}[/red]") @@ -8423,7 +9422,7 @@ async def run_list_collections() -> None: openwebui_config = StorageConfig( backend=StorageBackend.OPEN_WEBUI, endpoint=settings.openwebui_endpoint, - api_key=settings.openwebui_api_key, + api_key=SecretStr(settings.openwebui_api_key) if settings.openwebui_api_key else None, collection_name="default", ) openwebui = OpenWebUIStorage(openwebui_config) @@ -8432,7 +9431,8 @@ async def run_list_collections() -> None: overview = await openwebui.describe_collections() for item in overview: name = str(item.get("name", "Unknown")) - count = int(item.get("count", 0)) + count_val = item.get("count", 0) + count = int(count_val) if isinstance(count_val, (int, str)) else 0 openwebui_collections.append((name, count)) except Exception as e: console.print(f"❌ [red]OpenWebUI connection failed: {e}[/red]") @@ -8469,8 +9469,8 @@ async def run_search(query: str, collection: str | None, backend: str, limit: in """ Search across collections. """ - from ..config import configure_prefect, get_settings - from ..core.models import StorageBackend, StorageConfig + from ..config import get_settings + from ..core.models import StorageBackend from ..storage.weaviate import WeaviateStorage settings = get_settings() @@ -8487,7 +9487,7 @@ async def run_search(query: str, collection: str | None, backend: str, limit: in weaviate_config = StorageConfig( backend=StorageBackend.WEAVIATE, endpoint=settings.weaviate_endpoint, - api_key=settings.weaviate_api_key, + api_key=SecretStr(settings.weaviate_api_key) if settings.weaviate_api_key else None, collection_name=collection or "default", ) weaviate = WeaviateStorage(weaviate_config) @@ -8554,13 +9554,22 @@ from __future__ import annotations from contextlib import ExitStack -from prefect.settings import ( - PREFECT_API_KEY, - PREFECT_API_URL, - PREFECT_DEFAULT_WORK_POOL_NAME, - Setting, - temporary_settings, -) +from prefect.settings import Setting, temporary_settings + +try: + from prefect.settings import ( + PREFECT_API_KEY, + PREFECT_API_URL, + PREFECT_DEFAULT_WORK_POOL_NAME, + ) +except ImportError: + # Fallback for older Prefect versions that don't have these settings + from prefect.settings import PREFECT_SETTING_REGISTRY + + # Create fallback settings if they don't exist + PREFECT_API_KEY = PREFECT_SETTING_REGISTRY.get("PREFECT_API_KEY") or Setting("PREFECT_API_KEY", str, default=None) + PREFECT_API_URL = PREFECT_SETTING_REGISTRY.get("PREFECT_API_URL") or Setting("PREFECT_API_URL", str, default=None) + PREFECT_DEFAULT_WORK_POOL_NAME = PREFECT_SETTING_REGISTRY.get("PREFECT_DEFAULT_WORK_POOL_NAME") or Setting("PREFECT_DEFAULT_WORK_POOL_NAME", str, default=None) from .settings import Settings, get_settings @@ -8609,6 +9618,7 @@ def configure_prefect(settings: Settings) -> None: from functools import lru_cache from typing import Annotated, Literal +from prefect.variables import Variable from pydantic import Field, HttpUrl, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict @@ -8753,70 +9763,80 @@ def get_settings() -> Settings: Settings instance """ return Settings() - - - -"""Core module for ingestion pipeline.""" - -from .exceptions import ( - IngestionError, - StorageError, - VectorizationError, -) -from .models import ( - Document, - IngestionJob, - IngestionResult, - IngestionSource, - IngestionStatus, - StorageBackend, -) - -__all__ = [ - "Document", - "IngestionJob", - "IngestionResult", - "IngestionSource", - "IngestionStatus", - "StorageBackend", - "IngestionError", - "StorageError", - "VectorizationError", -] - - - -"""Custom exceptions for the ingestion pipeline.""" -class IngestionError(Exception): - """Base exception for ingestion errors.""" +class PrefectVariableConfig: + """Helper class for managing Prefect variables with fallbacks to settings.""" - pass + def __init__(self) -> None: + self._settings = get_settings() + self._variable_names = [ + "default_batch_size", "max_file_size", "max_crawl_depth", "max_crawl_pages", + "default_storage_backend", "default_collection_prefix", "max_concurrent_tasks", + "request_timeout", "default_schedule_interval" + ] + + def _get_fallback_value(self, name: str, default_value: object = None) -> object: + """Get fallback value from settings or default.""" + return default_value or getattr(self._settings, name, default_value) + + def get_with_fallback(self, name: str, default_value: str | int | float | None = None) -> str | int | float | None: + """Get variable value with fallback synchronously.""" + fallback = self._get_fallback_value(name, default_value) + # Ensure fallback is a type that Variable expects + variable_fallback = str(fallback) if fallback is not None else None + try: + result = Variable.get(name, default=variable_fallback) + # Variable can return various types, convert to our expected types + if isinstance(result, (str, int, float)): + return result + elif result is None: + return None + else: + # Convert other types to string + return str(result) + except Exception: + # Return fallback with proper type + if isinstance(fallback, (str, int, float)) or fallback is None: + return fallback + return str(fallback) if fallback is not None else None + + async def get_with_fallback_async(self, name: str, default_value: str | int | float | None = None) -> str | int | float | None: + """Get variable value with fallback asynchronously.""" + fallback = self._get_fallback_value(name, default_value) + variable_fallback = str(fallback) if fallback is not None else None + try: + result = await Variable.aget(name, default=variable_fallback) + # Variable can return various types, convert to our expected types + if isinstance(result, (str, int, float)): + return result + elif result is None: + return None + else: + # Convert other types to string + return str(result) + except Exception: + # Return fallback with proper type + if isinstance(fallback, (str, int, float)) or fallback is None: + return fallback + return str(fallback) if fallback is not None else None + + def get_ingestion_config(self) -> dict[str, str | int | float | None]: + """Get all ingestion-related configuration variables synchronously.""" + return {name: self.get_with_fallback(name) for name in self._variable_names} + + async def get_ingestion_config_async(self) -> dict[str, str | int | float | None]: + """Get all ingestion-related configuration variables asynchronously.""" + result = {} + for name in self._variable_names: + result[name] = await self.get_with_fallback_async(name) + return result -class StorageError(IngestionError): - """Exception for storage-related errors.""" - - pass - - -class VectorizationError(IngestionError): - """Exception for vectorization errors.""" - - pass - - -class ConfigurationError(IngestionError): - """Exception for configuration errors.""" - - pass - - -class SourceNotFoundError(IngestionError): - """Exception when source cannot be found or accessed.""" - - pass +@lru_cache +def get_prefect_config() -> PrefectVariableConfig: + """Get cached Prefect variable configuration helper.""" + return PrefectVariableConfig() @@ -8827,7 +9847,8 @@ from enum import Enum from typing import Annotated, TypedDict from uuid import UUID, uuid4 -from pydantic import BaseModel, Field, HttpUrl +from prefect.blocks.core import Block +from pydantic import BaseModel, Field, HttpUrl, SecretStr class IngestionStatus(str, Enum): @@ -8866,19 +9887,27 @@ class VectorConfig(BaseModel): batch_size: Annotated[int, Field(gt=0, le=1000)] = 100 -class StorageConfig(BaseModel): +class StorageConfig(Block): """Configuration for storage backend.""" + _block_type_name = "Storage Configuration" + _block_type_slug = "storage-config" + _description = "Configures storage backend connections and settings for document ingestion" + backend: StorageBackend endpoint: HttpUrl - api_key: str | None = Field(default=None) + api_key: SecretStr | None = Field(default=None) collection_name: str = Field(default="documents") batch_size: Annotated[int, Field(gt=0, le=1000)] = 100 -class FirecrawlConfig(BaseModel): +class FirecrawlConfig(Block): """Configuration for Firecrawl ingestion (operational parameters only).""" + _block_type_name = "Firecrawl Configuration" + _block_type_slug = "firecrawl-config" + _description = "Configures Firecrawl web scraping and crawling parameters" + formats: list[str] = Field(default_factory=lambda: ["markdown", "html"]) max_depth: Annotated[int, Field(ge=1, le=20)] = 5 limit: Annotated[int, Field(ge=1, le=1000)] = 100 @@ -8886,9 +9915,13 @@ class FirecrawlConfig(BaseModel): include_subdomains: bool = Field(default=False) -class RepomixConfig(BaseModel): +class RepomixConfig(Block): """Configuration for Repomix ingestion.""" + _block_type_name = "Repomix Configuration" + _block_type_slug = "repomix-config" + _description = "Configures repository ingestion patterns and file processing settings" + include_patterns: list[str] = Field( default_factory=lambda: ["*.py", "*.js", "*.ts", "*.md", "*.yaml", "*.json"] ) @@ -8899,27 +9932,75 @@ class RepomixConfig(BaseModel): respect_gitignore: bool = Field(default=True) -class R2RConfig(BaseModel): +class R2RConfig(Block): """Configuration for R2R ingestion.""" + _block_type_name = "R2R Configuration" + _block_type_slug = "r2r-config" + _description = "Configures R2R-specific ingestion settings including chunking and graph enrichment" + chunk_size: Annotated[int, Field(ge=100, le=8192)] = 1000 chunk_overlap: Annotated[int, Field(ge=0, le=1000)] = 200 enable_graph_enrichment: bool = Field(default=False) graph_creation_settings: dict[str, object] | None = Field(default=None) -class DocumentMetadata(TypedDict): - """Metadata for a document.""" - +class DocumentMetadataRequired(TypedDict): + """Required metadata fields for a document.""" source_url: str - title: str | None - description: str | None timestamp: datetime content_type: str word_count: int char_count: int +class DocumentMetadata(DocumentMetadataRequired, total=False): + """Rich metadata for a document with R2R-compatible fields.""" + + # Basic optional fields + title: str | None + description: str | None + + # Content categorization + tags: list[str] + category: str + section: str + language: str + + # Authorship and source info + author: str + domain: str + site_name: str + + # Document structure + heading_hierarchy: list[str] + section_depth: int + has_code_blocks: bool + has_images: bool + has_links: bool + + # Processing metadata + extraction_method: str + crawl_depth: int + last_modified: datetime | None + + # Content quality indicators + readability_score: float | None + completeness_score: float | None + + # Repository-specific fields + file_path: str | None + repository_name: str | None + branch_name: str | None + commit_hash: str | None + programming_language: str | None + + # Custom business metadata + importance_score: float | None + review_status: str | None + assigned_team: str | None + + class Document(BaseModel): """Represents a single document.""" @@ -8958,29 +10039,17 @@ class IngestionResult(BaseModel): error_messages: list[str] = Field(default_factory=list) - -"""Prefect flows for orchestration.""" - -from .ingestion import create_ingestion_flow -from .scheduler import create_scheduled_deployment - -__all__ = [ - "create_ingestion_flow", - "create_scheduled_deployment", -] - - """Prefect flow for ingestion pipeline.""" from __future__ import annotations -from datetime import UTC, datetime, timedelta -from typing import TYPE_CHECKING, Literal, assert_never, cast +from collections.abc import Callable +from datetime import UTC, datetime +from typing import TYPE_CHECKING, Literal, TypeAlias, assert_never, cast from prefect import flow, get_run_logger, task -from prefect.cache_policies import NO_CACHE -from prefect.futures import wait +from prefect.variables import Variable from ..config.settings import Settings from ..core.exceptions import IngestionError @@ -9003,8 +10072,8 @@ from ..utils.metadata_tagger import MetadataTagger SourceTypeLiteral = Literal["web", "repository", "documentation"] StorageBackendLiteral = Literal["weaviate", "open_webui", "r2r"] -SourceTypeLike = IngestionSource | SourceTypeLiteral -StorageBackendLike = StorageBackend | StorageBackendLiteral +SourceTypeLike: TypeAlias = IngestionSource | SourceTypeLiteral +StorageBackendLike: TypeAlias = StorageBackend | StorageBackendLiteral if TYPE_CHECKING: @@ -9037,16 +10106,20 @@ async def validate_source_task(source_url: str, source_type: IngestionSource) -> @task(name="initialize_storage", retries=3, retry_delay_seconds=5, tags=["storage"]) -async def initialize_storage_task(config: StorageConfig) -> BaseStorage: +async def initialize_storage_task(config: StorageConfig | str) -> BaseStorage: """ Initialize storage backend. Args: - config: Storage configuration + config: Storage configuration block or block name Returns: Initialized storage adapter """ + # Load block if string provided + if isinstance(config, str): + config = await StorageConfig.aload(config) + if config.backend == StorageBackend.WEAVIATE: storage = WeaviateStorage(config) elif config.backend == StorageBackend.OPEN_WEBUI: @@ -9062,40 +10135,46 @@ async def initialize_storage_task(config: StorageConfig) -> BaseStorage: return storage -@task(name="map_firecrawl_site", retries=2, retry_delay_seconds=15, tags=["firecrawl", "map"]) -async def map_firecrawl_site_task(source_url: str, config: FirecrawlConfig) -> list[str]: +@task(name="map_firecrawl_site", retries=2, retry_delay_seconds=15, tags=["firecrawl", "map"], + cache_key_fn=lambda ctx, p: f"firecrawl_map_{hash(p['source_url'])}") +async def map_firecrawl_site_task(source_url: str, config: FirecrawlConfig | str) -> list[str]: """Map a site using Firecrawl and return discovered URLs.""" + # Load block if string provided + if isinstance(config, str): + config = await FirecrawlConfig.aload(config) ingestor = FirecrawlIngestor(config) mapped = await ingestor.map_site(source_url) return mapped or [source_url] -@task(name="filter_existing_documents", retries=1, retry_delay_seconds=5, tags=["r2r", "dedup"], cache_policy=NO_CACHE) +@task(name="filter_existing_documents", retries=1, retry_delay_seconds=5, tags=["dedup"], + cache_key_fn=lambda ctx, p: f"filter_docs_{hash(str(p['urls']))}") # Cache based on URL list async def filter_existing_documents_task( urls: list[str], - storage_client: R2RStorageType, + storage_client: BaseStorage, stale_after_days: int = 30, + *, + collection_name: str | None = None, ) -> list[str]: - """Filter URLs whose documents are missing or stale in R2R.""" - + """Filter URLs to only those that need scraping (missing or stale in storage).""" logger = get_run_logger() - cutoff = datetime.now(UTC) - timedelta(days=stale_after_days) eligible: list[str] = [] for url in urls: document_id = str(FirecrawlIngestor.compute_document_id(url)) - existing: Document | None = await storage_client.retrieve(document_id) - if existing is None: - eligible.append(url) - continue + exists = await storage_client.check_exists( + document_id, + collection_name=collection_name, + stale_after_days=stale_after_days + ) - timestamp = existing.metadata["timestamp"] - if timestamp < cutoff: + if not exists: eligible.append(url) - if skipped := len(urls) - len(eligible): - logger.info("Skipping %s up-to-date pages", skipped) + skipped = len(urls) - len(eligible) + if skipped > 0: + logger.info("Skipping %s up-to-date documents in %s", skipped, storage_client.display_name) return eligible @@ -9107,9 +10186,9 @@ async def scrape_firecrawl_batch_task( batch_urls: list[str], config: FirecrawlConfig ) -> list[FirecrawlPage]: """Scrape a batch of URLs via Firecrawl.""" - ingestor = FirecrawlIngestor(config) - return await ingestor.scrape_pages(batch_urls) + pages = await ingestor.scrape_pages(batch_urls) + return cast(list[FirecrawlPage], pages) @task(name="annotate_firecrawl_metadata", retries=1, retry_delay_seconds=10, tags=["metadata"]) @@ -9117,7 +10196,6 @@ async def annotate_firecrawl_metadata_task( pages: list[FirecrawlPage], job: IngestionJob ) -> list[Document]: """Annotate scraped pages with standardized metadata.""" - if not pages: return [] @@ -9129,7 +10207,8 @@ async def annotate_firecrawl_metadata_task( settings = get_settings() async with MetadataTagger(llm_endpoint=str(settings.llm_endpoint)) as tagger: - return await tagger.tag_batch(documents) + tagged_docs = await tagger.tag_batch(documents) + return cast(list[Document], tagged_docs) except IngestionError as exc: # pragma: no cover - logging side effect logger = get_run_logger() logger.warning("Metadata tagging failed: %s", exc) @@ -9140,14 +10219,13 @@ async def annotate_firecrawl_metadata_task( return documents -@task(name="upsert_r2r_documents", retries=2, retry_delay_seconds=20, tags=["storage", "r2r"], cache_policy=NO_CACHE) +@task(name="upsert_r2r_documents", retries=2, retry_delay_seconds=20, tags=["storage", "r2r"]) async def upsert_r2r_documents_task( storage_client: R2RStorageType, documents: list[Document], collection_name: str | None, ) -> tuple[int, int]: """Upsert documents into R2R storage.""" - if not documents: return 0, 0 @@ -9164,12 +10242,15 @@ async def upsert_r2r_documents_task( return processed, failed -@task(name="ingest_documents", retries=2, retry_delay_seconds=30, tags=["ingestion"], cache_policy=NO_CACHE) +@task(name="ingest_documents", retries=2, retry_delay_seconds=30, tags=["ingestion"]) async def ingest_documents_task( job: IngestionJob, collection_name: str | None = None, - batch_size: int = 50, + batch_size: int | None = None, storage_client: BaseStorage | None = None, + storage_block_name: str | None = None, + ingestor_config_block_name: str | None = None, + progress_callback: Callable[[int, str], None] | None = None, ) -> tuple[int, int]: """ Ingest documents from source with optional pre-initialized storage client. @@ -9177,42 +10258,80 @@ async def ingest_documents_task( Args: job: Ingestion job configuration collection_name: Target collection name - batch_size: Number of documents per batch + batch_size: Number of documents per batch (uses Variable if None) storage_client: Optional pre-initialized storage client + storage_block_name: Optional storage block name to load + ingestor_config_block_name: Optional ingestor config block name to load + progress_callback: Optional callback for progress updates Returns: Tuple of (processed_count, failed_count) """ - ingestor = _create_ingestor(job) - storage = storage_client or await _create_storage(job, collection_name) + if progress_callback: + progress_callback(35, "Creating ingestor and storage clients...") - return await _process_documents(ingestor, storage, job, batch_size, collection_name) + # Use Variable for batch size if not provided + if batch_size is None: + try: + batch_size_var = await Variable.aget("default_batch_size", default="50") + # Convert Variable result to int, handling various types + if isinstance(batch_size_var, int): + batch_size = batch_size_var + elif isinstance(batch_size_var, (str, float)): + batch_size = int(float(str(batch_size_var))) + else: + batch_size = 50 + except Exception: + batch_size = 50 + + ingestor = await _create_ingestor(job, ingestor_config_block_name) + storage = storage_client or await _create_storage(job, collection_name, storage_block_name) + + if progress_callback: + progress_callback(40, "Starting document processing...") + + return await _process_documents(ingestor, storage, job, batch_size, collection_name, progress_callback) -def _create_ingestor(job: IngestionJob) -> BaseIngestor: +async def _create_ingestor(job: IngestionJob, config_block_name: str | None = None) -> BaseIngestor: """Create appropriate ingestor based on job source type.""" if job.source_type == IngestionSource.WEB: - config = FirecrawlConfig() + if config_block_name: + config = await FirecrawlConfig.aload(config_block_name) + else: + # Fallback to default configuration + config = FirecrawlConfig() return FirecrawlIngestor(config) elif job.source_type == IngestionSource.REPOSITORY: - config = RepomixConfig() + if config_block_name: + config = await RepomixConfig.aload(config_block_name) + else: + # Fallback to default configuration + config = RepomixConfig() return RepomixIngestor(config) else: raise ValueError(f"Unsupported source: {job.source_type}") -async def _create_storage(job: IngestionJob, collection_name: str | None) -> BaseStorage: +async def _create_storage(job: IngestionJob, collection_name: str | None, storage_block_name: str | None = None) -> BaseStorage: """Create and initialize storage client.""" if collection_name is None: - collection_name = f"docs_{job.source_type.value}" + # Use variable for default collection prefix + prefix = await Variable.aget("default_collection_prefix", default="docs") + collection_name = f"{prefix}_{job.source_type.value}" - from ..config import get_settings + if storage_block_name: + # Load storage config from block + storage_config = await StorageConfig.aload(storage_block_name) + # Override collection name if provided + storage_config.collection_name = collection_name + else: + # Fallback to building config from settings + from ..config import get_settings + settings = get_settings() + storage_config = _build_storage_config(job, settings, collection_name) - settings = get_settings() - - storage_config = _build_storage_config(job, settings, collection_name) storage = _instantiate_storage(job.storage_backend, storage_config) - await storage.initialize() return storage @@ -9281,16 +10400,43 @@ async def _process_documents( job: IngestionJob, batch_size: int, collection_name: str | None, + progress_callback: Callable[[int, str], None] | None = None, ) -> tuple[int, int]: """Process documents in batches.""" processed = 0 failed = 0 batch: list[Document] = [] + total_documents = 0 + batch_count = 0 - async for document in ingestor.ingest(job): + if progress_callback: + progress_callback(45, "Ingesting documents from source...") + + # Use smart ingestion with deduplication if supported and beneficial + if hasattr(ingestor, 'ingest_with_dedup') and hasattr(storage, 'check_exists'): + try: + # Try to use the smart ingestion method + document_generator = ingestor.ingest_with_dedup( + job, storage, collection_name=collection_name + ) + except Exception: + # Fall back to regular ingestion if smart method fails + document_generator = ingestor.ingest(job) + else: + document_generator = ingestor.ingest(job) + + async for document in document_generator: batch.append(document) + total_documents += 1 if len(batch) >= batch_size: + batch_count += 1 + if progress_callback: + progress_callback( + 45 + min(35, (batch_count * 10)), + f"Processing batch {batch_count} ({total_documents} documents so far)..." + ) + batch_processed, batch_failed = await _store_batch(storage, batch, collection_name) processed += batch_processed failed += batch_failed @@ -9298,10 +10444,17 @@ async def _process_documents( # Process remaining batch if batch: + batch_count += 1 + if progress_callback: + progress_callback(80, f"Processing final batch ({total_documents} total documents)...") + batch_processed, batch_failed = await _store_batch(storage, batch, collection_name) processed += batch_processed failed += batch_failed + if progress_callback: + progress_callback(85, f"Completed processing {total_documents} documents") + return processed, failed @@ -9351,62 +10504,96 @@ async def _store_batch( log_prints=True, ) async def firecrawl_to_r2r_flow( - job: IngestionJob, collection_name: str | None = None + job: IngestionJob, collection_name: str | None = None, progress_callback: Callable[[int, str], None] | None = None ) -> tuple[int, int]: """Specialized flow for Firecrawl ingestion into R2R.""" - logger = get_run_logger() from ..config import get_settings + if progress_callback: + progress_callback(35, "Initializing Firecrawl and R2R storage...") + settings = get_settings() firecrawl_config = FirecrawlConfig() resolved_collection = collection_name or f"docs_{job.source_type.value}" storage_config = _build_storage_config(job, settings, resolved_collection) - storage_future = initialize_storage_task.submit(storage_config) - mapping_future = map_firecrawl_site_task.submit(str(job.source_url), firecrawl_config) + storage_client = await initialize_storage_task(storage_config) - storage_client = cast(BaseStorage, storage_future.result()) if RuntimeR2RStorage is None or not isinstance(storage_client, RuntimeR2RStorage): raise IngestionError("Firecrawl to R2R flow requires an R2R storage backend") r2r_storage = cast("R2RStorageType", storage_client) - discovered_urls = cast(list[str], mapping_future.result()) + if progress_callback: + progress_callback(45, "Checking for existing content before mapping...") + + # Smart mapping: try single URL first to avoid expensive map operation + base_url = str(job.source_url) + single_url_id = str(FirecrawlIngestor.compute_document_id(base_url)) + base_exists = await r2r_storage.check_exists( + single_url_id, collection_name=resolved_collection, stale_after_days=30 + ) + + if base_exists: + # Check if this is a recent single-page update + logger.info("Base URL %s exists and is fresh, skipping expensive mapping", base_url) + if progress_callback: + progress_callback(100, "Content is up to date, no processing needed") + return 0, 0 + + if progress_callback: + progress_callback(50, "Discovering pages with Firecrawl...") + + discovered_urls = await map_firecrawl_site_task(base_url, firecrawl_config) unique_urls = _deduplicate_urls(discovered_urls) logger.info("Discovered %s unique URLs from Firecrawl map", len(unique_urls)) - eligibility_future = filter_existing_documents_task.submit(unique_urls, r2r_storage) - eligible_urls = cast(list[str], eligibility_future.result()) + if progress_callback: + progress_callback(60, f"Found {len(unique_urls)} pages, filtering existing content...") + + eligible_urls = await filter_existing_documents_task( + unique_urls, r2r_storage, collection_name=resolved_collection + ) if not eligible_urls: logger.info("All Firecrawl pages are up to date for %s", job.source_url) + if progress_callback: + progress_callback(100, "All pages are up to date, no processing needed") return 0, 0 + if progress_callback: + progress_callback(70, f"Scraping {len(eligible_urls)} new/updated pages...") + batch_size = min(settings.default_batch_size, firecrawl_config.limit) url_batches = _chunk_urls(eligible_urls, batch_size) logger.info("Scraping %s batches of Firecrawl pages", len(url_batches)) - scrape_futures = [ - scrape_firecrawl_batch_task.submit(batch, firecrawl_config) + # Use asyncio.gather for concurrent scraping + import asyncio + scrape_tasks = [ + scrape_firecrawl_batch_task(batch, firecrawl_config) for batch in url_batches ] - done, _ = wait(scrape_futures) + batch_results = await asyncio.gather(*scrape_tasks) scraped_pages: list[FirecrawlPage] = [] - for future in done: - batch_pages = cast(list[FirecrawlPage], future.result()) + for batch_pages in batch_results: scraped_pages.extend(batch_pages) - documents_future = annotate_firecrawl_metadata_task.submit(scraped_pages, job) - documents = cast(list[Document], documents_future.result()) + if progress_callback: + progress_callback(80, f"Processing {len(scraped_pages)} scraped pages...") + + documents = await annotate_firecrawl_metadata_task(scraped_pages, job) if not documents: logger.warning("No documents produced after scraping for %s", job.source_url) return 0, len(eligible_urls) - upsert_future = upsert_r2r_documents_task.submit(r2r_storage, documents, resolved_collection) - processed, failed = cast(tuple[int, int], upsert_future.result()) + if progress_callback: + progress_callback(90, f"Storing {len(documents)} documents in R2R...") + + processed, failed = await upsert_r2r_documents_task(r2r_storage, documents, resolved_collection) logger.info("Upserted %s documents into R2R (%s failed)", processed, failed) @@ -9414,7 +10601,7 @@ async def firecrawl_to_r2r_flow( @task(name="update_job_status", tags=["tracking"]) -async def update_job_status_task( +def update_job_status_task( job: IngestionJob, status: IngestionStatus, processed: int = 0, @@ -9461,6 +10648,7 @@ async def create_ingestion_flow( storage_backend: StorageBackendLike = StorageBackend.WEAVIATE, collection_name: str | None = None, validate_first: bool = True, + progress_callback: Callable[[int, str], None] | None = None, ) -> IngestionResult: """ Main ingestion flow. @@ -9470,6 +10658,7 @@ async def create_ingestion_flow( source_type: Type of source storage_backend: Storage backend to use validate_first: Whether to validate source first + progress_callback: Optional callback for progress updates Returns: Ingestion result @@ -9495,6 +10684,8 @@ async def create_ingestion_flow( try: # Validate source if requested if validate_first: + if progress_callback: + progress_callback(10, "Validating source...") print("Validating source...") is_valid = await validate_source_task(source_url, job.source_type) @@ -9502,14 +10693,21 @@ async def create_ingestion_flow( raise IngestionError(f"Source validation failed: {source_url}") # Update status to in progress - job = await update_job_status_task(job, IngestionStatus.IN_PROGRESS) + if progress_callback: + progress_callback(20, "Initializing storage...") + job = cast(IngestionJob, update_job_status_task(job, IngestionStatus.IN_PROGRESS)) # Run ingestion + if progress_callback: + progress_callback(30, "Starting document ingestion...") print("Ingesting documents...") if job.source_type == IngestionSource.WEB and job.storage_backend == StorageBackend.R2R: - processed, failed = await firecrawl_to_r2r_flow(job, collection_name) + processed, failed = await firecrawl_to_r2r_flow(job, collection_name, progress_callback=progress_callback) else: - processed, failed = await ingest_documents_task(job, collection_name) + processed, failed = await ingest_documents_task(job, collection_name, progress_callback=progress_callback) + + if progress_callback: + progress_callback(90, "Finalizing ingestion...") # Update final status if failed > 0: @@ -9523,7 +10721,7 @@ async def create_ingestion_flow( else: final_status = IngestionStatus.COMPLETED - job = await update_job_status_task(job, final_status, processed=processed, _failed=failed) + job = cast(IngestionJob, update_job_status_task(job, final_status, processed=processed, _failed=failed)) print(f"Ingestion completed: {processed} processed, {failed} failed") @@ -9532,9 +10730,9 @@ async def create_ingestion_flow( error_messages.append(str(e)) # Don't reset counts - keep whatever was processed before the error - job = await update_job_status_task( + job = cast(IngestionJob, update_job_status_task( job, IngestionStatus.FAILED, processed=processed, _failed=failed, error=str(e) - ) + )) # Calculate duration duration = (datetime.now(UTC) - start_time).total_seconds() @@ -9558,6 +10756,7 @@ from typing import Literal, Protocol, cast from prefect import serve from prefect.deployments.runner import RunnerDeployment from prefect.schedules import Cron, Interval +from prefect.variables import Variable from ..core.models import IngestionSource, StorageBackend from .ingestion import SourceTypeLike, StorageBackendLike, create_ingestion_flow @@ -9582,11 +10781,13 @@ def create_scheduled_deployment( storage_backend: StorageBackendLike = StorageBackend.WEAVIATE, schedule_type: Literal["cron", "interval"] = "interval", cron_expression: str | None = None, - interval_minutes: int = 60, + interval_minutes: int | None = None, tags: list[str] | None = None, + storage_block_name: str | None = None, + ingestor_config_block_name: str | None = None, ) -> RunnerDeployment: """ - Create a scheduled deployment for ingestion. + Create a scheduled deployment for ingestion with block support. Args: name: Deployment name @@ -9595,12 +10796,28 @@ def create_scheduled_deployment( storage_backend: Storage backend schedule_type: Type of schedule cron_expression: Cron expression if using cron - interval_minutes: Interval in minutes if using interval + interval_minutes: Interval in minutes (uses Variable if None) tags: Optional tags for deployment + storage_block_name: Optional storage block name + ingestor_config_block_name: Optional ingestor config block name Returns: Deployment configuration """ + # Use Variable for interval if not provided + if interval_minutes is None: + try: + interval_var = Variable.get("default_schedule_interval", default="60") + # Convert Variable result to int, handling various types + if isinstance(interval_var, int): + interval_minutes = interval_var + elif isinstance(interval_var, (str, float)): + interval_minutes = int(float(str(interval_var))) + else: + interval_minutes = 60 + except Exception: + interval_minutes = 60 + # Create schedule if schedule_type == "cron" and cron_expression: schedule = Cron(cron_expression, timezone="UTC") @@ -9614,18 +10831,27 @@ def create_scheduled_deployment( if tags is None: tags = [source_enum.value, backend_enum.value] + # Create deployment parameters with block support + parameters = { + "source_url": source_url, + "source_type": source_enum.value, + "storage_backend": backend_enum.value, + "validate_first": True, + } + + # Add block names if provided + if storage_block_name: + parameters["storage_block_name"] = storage_block_name + if ingestor_config_block_name: + parameters["ingestor_config_block_name"] = ingestor_config_block_name + # Create deployment # The flow decorator adds the to_deployment method at runtime to_deployment = create_ingestion_flow.to_deployment deployment = to_deployment( name=name, schedule=schedule, - parameters={ - "source_url": source_url, - "source_type": source_enum.value, - "storage_backend": backend_enum.value, - "validate_first": True, - }, + parameters=parameters, tags=tags, description=f"Scheduled ingestion from {source_url}", ) @@ -9715,14 +10941,15 @@ class BaseIngestor(ABC): import asyncio import logging -from collections.abc import AsyncGenerator +import re +from collections.abc import AsyncGenerator, Awaitable, Callable from dataclasses import dataclass from datetime import UTC, datetime -from functools import wraps +from typing import TYPE_CHECKING +from urllib.parse import urlparse from uuid import NAMESPACE_URL, UUID, uuid5 from firecrawl import AsyncFirecrawl -from collections.abc import Awaitable, Callable from typing_extensions import override from ..config import get_settings @@ -9736,8 +10963,11 @@ from ..core.models import ( ) from .base import BaseIngestor +if TYPE_CHECKING: + from ..storage.base import BaseStorage -class FirecrawlError(IngestionError): # type: ignore[misc] + +class FirecrawlError(IngestionError): """Base exception for Firecrawl-related errors.""" def __init__(self, message: str, status_code: int | None = None) -> None: @@ -9747,20 +10977,25 @@ class FirecrawlError(IngestionError): # type: ignore[misc] class FirecrawlConnectionError(FirecrawlError): """Connection error with Firecrawl service.""" + pass class FirecrawlRateLimitError(FirecrawlError): """Rate limit exceeded error.""" + pass class FirecrawlUnauthorizedError(FirecrawlError): """Unauthorized access error.""" + pass -async def retry_with_backoff(operation: Callable[[], Awaitable[object]], max_retries: int = 3) -> object: +async def retry_with_backoff( + operation: Callable[[], Awaitable[object]], max_retries: int = 3 +) -> object: """Retry operation with exponential backoff following Firecrawl best practices.""" for attempt in range(max_retries): try: @@ -9768,8 +11003,10 @@ async def retry_with_backoff(operation: Callable[[], Awaitable[object]], max_ret except Exception as e: if attempt == max_retries - 1: raise e - delay = 1.0 * (2 ** attempt) - logging.warning(f"Firecrawl operation failed (attempt {attempt + 1}/{max_retries}): {e}. Retrying in {delay:.1f}s...") + delay = 1.0 * (2**attempt) + logging.warning( + f"Firecrawl operation failed (attempt {attempt + 1}/{max_retries}): {e}. Retrying in {delay:.1f}s..." + ) await asyncio.sleep(delay) # This should never be reached due to the exception handling above, @@ -9785,6 +11022,21 @@ class FirecrawlPage: content: str title: str | None description: str | None + author: str | None = None + language: str | None = None + sitemap_last_modified: str | None = None + source_url: str | None = None + keywords: list[str] | None = None + robots: str | None = None + og_title: str | None = None + og_description: str | None = None + og_url: str | None = None + og_image: str | None = None + twitter_card: str | None = None + twitter_site: str | None = None + twitter_creator: str | None = None + favicon: str | None = None + status_code: int | None = None class FirecrawlIngestor(BaseIngestor): @@ -9812,10 +11064,14 @@ class FirecrawlIngestor(BaseIngestor): # Note: api_url parameter may not be supported in all versions # Default to standard initialization for cloud instances try: - endpoint_str = str(settings.firecrawl_endpoint).rstrip('/') - if endpoint_str.startswith("http://crawl.lab") or endpoint_str.startswith("http://localhost"): + endpoint_str = str(settings.firecrawl_endpoint).rstrip("/") + if endpoint_str.startswith("http://crawl.lab") or endpoint_str.startswith( + "http://localhost" + ): # Self-hosted instance - try with api_url if supported - self.client = AsyncFirecrawl(api_key=api_key, api_url=str(settings.firecrawl_endpoint)) + self.client = AsyncFirecrawl( + api_key=api_key, api_url=str(settings.firecrawl_endpoint) + ) else: # Cloud instance - use standard initialization self.client = AsyncFirecrawl(api_key=api_key) @@ -9848,6 +11104,55 @@ class FirecrawlIngestor(BaseIngestor): for page in pages: yield self.create_document(page, job) + async def ingest_with_dedup( + self, + job: IngestionJob, + storage_client: "BaseStorage", + *, + collection_name: str | None = None, + stale_after_days: int = 30, + ) -> AsyncGenerator[Document, None]: + """ + Ingest documents with duplicate detection to avoid unnecessary scraping. + + Args: + job: The ingestion job configuration + storage_client: Storage client to check for existing documents + collection_name: Collection to check for duplicates + stale_after_days: Consider documents stale after this many days + + Yields: + Documents from the web source (only new/stale ones) + """ + url = str(job.source_url) + + # First, map the site to understand its structure + site_map = await self.map_site(url) or [url] + + # Filter out URLs that already exist in storage and are fresh + eligible_urls: list[str] = [] + for check_url in site_map: + document_id = str(self.compute_document_id(check_url)) + exists = await storage_client.check_exists( + document_id, + collection_name=collection_name, + stale_after_days=stale_after_days + ) + if not exists: + eligible_urls.append(check_url) + + if not eligible_urls: + return # No new documents to scrape + + # Process eligible pages in batches + batch_size = 10 + for i in range(0, len(eligible_urls), batch_size): + batch_urls = eligible_urls[i : i + batch_size] + pages = await self.scrape_pages(batch_urls) + + for page in pages: + yield self.create_document(page, job) + async def map_site(self, url: str) -> list[str]: """Public wrapper for mapping a site.""" @@ -9915,10 +11220,7 @@ class FirecrawlIngestor(BaseIngestor): if result and getattr(result, "links", None): # Extract URLs from the result following official pattern - return [ - getattr(link, "url", str(link)) - for link in result.links - ] + return [getattr(link, "url", str(link)) for link in result.links] return [] except Exception as e: # If map fails (might not be available in all versions), fall back to single URL @@ -9950,13 +11252,13 @@ class FirecrawlIngestor(BaseIngestor): async def _scrape_single(self, url: str) -> FirecrawlPage | None: """ - Scrape a single URL. + Scrape a single URL and extract rich metadata. Args: url: URL to scrape Returns: - Scraped document data + Scraped document data with enhanced metadata """ try: # Use SDK v2 scrape endpoint following official pattern with retry @@ -9967,14 +11269,60 @@ class FirecrawlIngestor(BaseIngestor): if result: # The SDK returns a ScrapeData object with typed metadata metadata = getattr(result, "metadata", None) + + # Extract basic metadata title = getattr(metadata, "title", None) if metadata else None description = getattr(metadata, "description", None) if metadata else None + # Extract enhanced metadata if available + author = getattr(metadata, "author", None) if metadata else None + language = getattr(metadata, "language", None) if metadata else None + sitemap_last_modified = ( + getattr(metadata, "sitemap_last_modified", None) if metadata else None + ) + source_url = getattr(metadata, "sourceURL", None) if metadata else None + keywords = getattr(metadata, "keywords", None) if metadata else None + robots = getattr(metadata, "robots", None) if metadata else None + + # Open Graph metadata + og_title = getattr(metadata, "ogTitle", None) if metadata else None + og_description = getattr(metadata, "ogDescription", None) if metadata else None + og_url = getattr(metadata, "ogUrl", None) if metadata else None + og_image = getattr(metadata, "ogImage", None) if metadata else None + + # Twitter metadata + twitter_card = getattr(metadata, "twitterCard", None) if metadata else None + twitter_site = getattr(metadata, "twitterSite", None) if metadata else None + twitter_creator = ( + getattr(metadata, "twitterCreator", None) if metadata else None + ) + + # Additional metadata + favicon = getattr(metadata, "favicon", None) if metadata else None + status_code = getattr(metadata, "statusCode", None) if metadata else None + return FirecrawlPage( url=url, content=getattr(result, "markdown", "") or "", title=title, description=description, + author=author, + language=language, + sitemap_last_modified=sitemap_last_modified, + source_url=source_url, + keywords=keywords.split(",") + if keywords and isinstance(keywords, str) + else keywords, + robots=robots, + og_title=og_title, + og_description=og_description, + og_url=og_url, + og_image=og_image, + twitter_card=twitter_card, + twitter_site=twitter_site, + twitter_creator=twitter_creator, + favicon=favicon, + status_code=status_code, ) return None @@ -9987,33 +11335,159 @@ class FirecrawlIngestor(BaseIngestor): @staticmethod def compute_document_id(source_url: str) -> UUID: """Derive a deterministic UUID for a document based on its source URL.""" - return uuid5(NAMESPACE_URL, source_url) + @staticmethod + def _analyze_content_structure(content: str) -> dict[str, object]: + """Analyze markdown content to extract structural information.""" + # Extract heading hierarchy + heading_pattern = r"^(#{1,6})\s+(.+)$" + headings = [] + for match in re.finditer(heading_pattern, content, re.MULTILINE): + level = len(match.group(1)) + text = match.group(2).strip() + headings.append(f"{' ' * (level - 1)}{text}") + + # Check for various content types + has_code_blocks = bool(re.search(r"```[\s\S]*?```", content)) + has_images = bool(re.search(r"!\[.*?\]\(.*?\)", content)) + has_links = bool(re.search(r"\[.*?\]\(.*?\)", content)) + + # Calculate section depth + max_depth = 0 + if headings: + for heading in headings: + depth = (len(heading) - len(heading.lstrip())) // 2 + 1 + max_depth = max(max_depth, depth) + + return { + "heading_hierarchy": headings, + "section_depth": max_depth, + "has_code_blocks": has_code_blocks, + "has_images": has_images, + "has_links": has_links, + } + + @staticmethod + def _calculate_content_quality(content: str, title: str | None) -> dict[str, float | None]: + """Calculate basic content quality metrics.""" + if not content: + return {"readability_score": None, "completeness_score": None} + + # Simple readability approximation (Flesch-like) + sentences = len(re.findall(r"[.!?]+", content)) + words = len(content.split()) + + if sentences == 0 or words == 0: + readability_score = None + else: + avg_sentence_length = words / sentences + # Simplified readability score (0-100, higher is more readable) + readability_score = max(0, min(100, 100 - (avg_sentence_length - 15) * 2)) + + # Completeness score based on structure + completeness_factors = 0 + total_factors = 5 + + if title: + completeness_factors += 1 + if len(content) > 500: + completeness_factors += 1 + if re.search(r"^#{1,6}\s+", content, re.MULTILINE): + completeness_factors += 1 + if len(content.split()) > 100: + completeness_factors += 1 + if not re.search(r"(error|404|not found|page not found)", content, re.IGNORECASE): + completeness_factors += 1 + + completeness_score = (completeness_factors / total_factors) * 100 + + return { + "readability_score": readability_score, + "completeness_score": completeness_score, + } + + @staticmethod + def _extract_domain_info(url: str) -> dict[str, str]: + """Extract domain and site information from URL.""" + parsed = urlparse(url) + domain = parsed.netloc.lower() + + # Remove www. prefix + if domain.startswith("www."): + domain = domain[4:] + + # Extract site name from domain + domain_parts = domain.split(".") + site_name = domain_parts[0].replace("-", " ").replace("_", " ").title() + + return { + "domain": domain, + "site_name": site_name, + } + def create_document(self, page: FirecrawlPage, job: IngestionJob) -> Document: """ - Create a Document from scraped data. + Create a Document from scraped data with enriched metadata. Args: page: Scraped document data job: The ingestion job Returns: - Document instance + Document instance with rich metadata """ content = page.content source_url = page.url + # Analyze content structure + structure_info = self._analyze_content_structure(content) + + # Calculate quality metrics + quality_info = self._calculate_content_quality(content, page.title) + + # Extract domain information + domain_info = self._extract_domain_info(source_url) + + # Build rich metadata metadata: DocumentMetadata = { + # Core required fields "source_url": source_url, - "title": page.title, - "description": page.description, "timestamp": datetime.now(UTC), "content_type": "text/markdown", "word_count": len(content.split()), "char_count": len(content), + # Basic optional fields + "title": page.title or f"Page from {source_url}", + "description": page.description + or page.og_description + or f"Content scraped from {source_url}", + # Content categorization + "tags": page.keywords or [], + "language": page.language or "en", + # Authorship and source info + "author": page.author or page.twitter_creator or "Unknown", + "domain": domain_info["domain"], + "site_name": domain_info["site_name"], + # Document structure + "heading_hierarchy": structure_info["heading_hierarchy"], + "section_depth": structure_info["section_depth"], + "has_code_blocks": structure_info["has_code_blocks"], + "has_images": structure_info["has_images"], + "has_links": structure_info["has_links"], + # Processing metadata + "extraction_method": "firecrawl", + "last_modified": datetime.fromisoformat(page.sitemap_last_modified) + if page.sitemap_last_modified + else None, + # Content quality indicators + "readability_score": quality_info["readability_score"], + "completeness_score": quality_info["completeness_score"], } + # Note: Additional web-specific metadata like og_title, twitter_card etc. + # would need to be added to DocumentMetadata TypedDict if needed + return Document( id=self.compute_document_id(source_url), content=content, @@ -10026,12 +11500,12 @@ class FirecrawlIngestor(BaseIngestor): """Close the Firecrawl client and cleanup resources.""" # AsyncFirecrawl may not have explicit close method in all versions # This is defensive cleanup following best practices - if hasattr(self.client, 'close'): + if hasattr(self.client, "close"): try: await self.client.close() except Exception as e: logging.debug(f"Error closing Firecrawl client: {e}") - elif hasattr(self.client, '_session') and hasattr(self.client._session, 'close'): + elif hasattr(self.client, "_session") and hasattr(self.client._session, "close"): try: await self.client._session.close() except Exception as e: @@ -10041,7 +11515,12 @@ class FirecrawlIngestor(BaseIngestor): """Async context manager entry.""" return self - async def __aexit__(self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: object | None) -> None: + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: object | None, + ) -> None: """Async context manager exit with cleanup.""" await self.close() @@ -10151,6 +11630,38 @@ class BaseStorage(ABC): """ raise NotImplementedError(f"{self.__class__.__name__} doesn't support document retrieval") + async def check_exists( + self, document_id: str, *, collection_name: str | None = None, stale_after_days: int = 30 + ) -> bool: + """ + Check if a document exists and is not stale. + + Args: + document_id: Document ID to check + collection_name: Collection to check in + stale_after_days: Consider document stale after this many days + + Returns: + True if document exists and is not stale, False otherwise + """ + try: + document = await self.retrieve(document_id, collection_name=collection_name) + if document is None: + return False + + # Check staleness if timestamp is available + if "timestamp" in document.metadata: + from datetime import UTC, datetime, timedelta + timestamp = document.metadata["timestamp"] + cutoff = datetime.now(UTC) - timedelta(days=stale_after_days) + return timestamp >= cutoff + + # If no timestamp, assume it exists and is valid + return True + except Exception: + # Backend doesn't support retrieval, assume doesn't exist + return False + def search( self, query: str, @@ -10206,6 +11717,15 @@ class BaseStorage(ABC): """ return [] + async def describe_collections(self) -> list[dict[str, object]]: + """ + Describe available collections with metadata (if supported by backend). + + Returns: + List of collection metadata dictionaries, empty list if not supported + """ + return [] + async def list_documents( self, limit: int = 100, @@ -10235,7 +11755,8 @@ class BaseStorage(ABC): Default implementation does nothing. """ - pass + # Default implementation - storage backends can override to cleanup connections + return None @@ -10243,7 +11764,7 @@ class BaseStorage(ABC): import asyncio import logging -from typing import Final, cast +from typing import Final, TypedDict, cast import httpx from typing_extensions import override @@ -10396,7 +11917,12 @@ class OpenWebUIStorage(BaseStorage): raise StorageError("Knowledge base not initialized") # Step 1: Upload document as file - files = {"file": (f"{document.id}.txt", document.content.encode(), "text/plain")} + # Use document title from metadata if available, otherwise fall back to ID + filename = document.metadata.get("title") or f"doc_{document.id}" + # Ensure filename has proper extension + if not filename.endswith(('.txt', '.md', '.pdf', '.doc', '.docx')): + filename = f"{filename}.txt" + files = {"file": (filename, document.content.encode(), "text/plain")} response = await self.client.post( "/api/v1/files/", files=files, @@ -10449,7 +11975,12 @@ class OpenWebUIStorage(BaseStorage): raise StorageError("Knowledge base not initialized") async def upload_and_attach(doc: Document) -> str: - files = {"file": (f"{doc.id}.txt", doc.content.encode(), "text/plain")} + # Use document title from metadata if available, otherwise fall back to ID + filename = doc.metadata.get("title") or f"doc_{doc.id}" + # Ensure filename has proper extension + if not filename.endswith(('.txt', '.md', '.pdf', '.doc', '.docx')): + filename = f"{filename}.txt" + files = {"file": (filename, doc.content.encode(), "text/plain")} upload_response = await self.client.post( "/api/v1/files/", files=files, @@ -10505,6 +12036,25 @@ class OpenWebUIStorage(BaseStorage): except Exception as e: raise StorageError(f"Failed to store batch: {e}") from e + @override + async def retrieve( + self, document_id: str, *, collection_name: str | None = None + ) -> Document | None: + """ + OpenWebUI doesn't support document retrieval by ID. + + Args: + document_id: File ID (not supported) + collection_name: Collection name (not used) + + Returns: + Always None - retrieval not supported + """ + # OpenWebUI uses file-based storage without direct document retrieval + # This will cause the base check_exists method to return False, + # which means documents will always be re-scraped for OpenWebUI + raise NotImplementedError("OpenWebUI doesn't support document retrieval by ID") + @override async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: """ @@ -10638,36 +12188,128 @@ class OpenWebUIStorage(BaseStorage): LOGGER.error("Unexpected error deleting knowledge base %s", collection_name, exc_info=e) return False - async def describe_collections(self) -> list[dict[str, str | int | float]]: + class CollectionSummary(TypedDict): + """Structure describing a knowledge base summary.""" + + name: str + count: int + size_mb: float + + async def describe_collections(self) -> list[dict[str, object]]: """Return metadata about each knowledge base.""" try: + # First get the list of knowledge bases response = await self.client.get("/api/v1/knowledge/") response.raise_for_status() knowledge_bases = response.json() - collections: list[dict[str, str | int | float]] = [] + LOGGER.info(f"OpenWebUI returned {len(knowledge_bases)} knowledge bases") + LOGGER.debug(f"Knowledge bases structure: {knowledge_bases}") + + collections: list[dict[str, object]] = [] for kb in knowledge_bases: if not isinstance(kb, dict): continue + + kb_id = kb.get("id") name = kb.get("name", "Unknown") - files = kb.get("files", []) - if files is None: - files = [] - count = len(files) if isinstance(files, list) else 0 + + LOGGER.info(f"Processing knowledge base: '{name}' (ID: {kb_id})") + LOGGER.debug(f"KB structure: {kb}") + + if not kb_id: + # If no ID, fall back to basic count from list response + files = kb.get("files", []) + if files is None: + files = [] + count = len(files) if isinstance(files, list) else 0 + else: + # Get detailed knowledge base information using the correct endpoint + try: + LOGGER.debug(f"Fetching detailed info for KB '{name}' from /api/v1/knowledge/{kb_id}") + detail_response = await self.client.get(f"/api/v1/knowledge/{kb_id}") + detail_response.raise_for_status() + detailed_kb = detail_response.json() + + LOGGER.debug(f"Detailed KB response: {detailed_kb}") + + files = detailed_kb.get("files", []) + if files is None: + files = [] + count = len(files) if isinstance(files, list) else 0 + + # Debug logging + LOGGER.info(f"Knowledge base '{name}' (ID: {kb_id}): found {count} files") + if count > 0 and len(files) > 0: + LOGGER.debug(f"First file structure: {files[0] if files else 'No files'}") + elif count == 0: + LOGGER.warning(f"Knowledge base '{name}' has 0 files. Files field type: {type(files)}, value: {files}") + + except Exception as e: + LOGGER.warning(f"Failed to get detailed info for KB '{name}' (ID: {kb_id}): {e}") + # Fallback to basic files list if detailed fetch fails + files = kb.get("files", []) + if files is None: + files = [] + count = len(files) if isinstance(files, list) else 0 + LOGGER.info(f"Fallback count for KB '{name}': {count}") + size_mb = count * 0.5 # rough heuristic - collections.append( - { - "name": name, - "count": count, - "size_mb": size_mb, - } - ) + summary: dict[str, object] = { + "name": str(name), + "count": count, + "size_mb": float(size_mb), + } + collections.append(summary) return collections except Exception as e: raise StorageError(f"Failed to describe knowledge bases: {e}") from e + async def count(self, *, collection_name: str | None = None) -> int: + """ + Get document count for a specific collection (knowledge base). + + Args: + collection_name: Name of the knowledge base to count documents for + + Returns: + Number of documents in the collection, 0 if collection not found + """ + if not collection_name: + # If no collection name provided, return total across all collections + try: + collections = await self.describe_collections() + return sum(collection["count"] for collection in collections) + except Exception: + return 0 + + try: + # Get knowledge base by name and return its file count + kb = await self.get_knowledge_by_name(collection_name) + if not kb: + return 0 + + kb_id = kb.get("id") + if not kb_id: + return 0 + + # Get detailed knowledge base information to get accurate file count + detail_response = await self.client.get(f"/api/v1/knowledge/{kb_id}") + detail_response.raise_for_status() + detailed_kb = detail_response.json() + + files = detailed_kb.get("files", []) + count = len(files) if isinstance(files, list) else 0 + + LOGGER.debug(f"Count for collection '{collection_name}': {count} files") + return count + + except Exception as e: + LOGGER.warning(f"Failed to get count for collection '{collection_name}': {e}") + return 0 + async def get_knowledge_by_name(self, name: str) -> dict[str, object] | None: """ Get knowledge base details by name. @@ -10771,8 +12413,31 @@ class OpenWebUIStorage(BaseStorage): # Safely extract fields with fallbacks doc_id = str(file_info.get("id", f"file_{i}")) - filename = str(file_info.get("filename") or file_info.get("name", f"file_{i}")) - size = file_info.get("size", 0) + + # Try multiple ways to get filename from OpenWebUI API response + filename = None + # Check direct filename field + if "filename" in file_info: + filename = file_info["filename"] + # Check name field + elif "name" in file_info: + filename = file_info["name"] + # Check meta.name (from FileModelResponse schema) + elif isinstance(file_info.get("meta"), dict): + filename = file_info["meta"].get("name") + + # Final fallback + if not filename: + filename = f"file_{i}" + + filename = str(filename) + + # Extract size from meta if available + size = 0 + if isinstance(file_info.get("meta"), dict): + size = file_info["meta"].get("size", 0) + else: + size = file_info.get("size", 0) # Estimate word count from file size (very rough approximation) word_count = max(1, int(size / 6)) if isinstance(size, (int, float)) else 0 @@ -10829,9 +12494,9 @@ class OpenWebUIStorage(BaseStorage): """Weaviate storage adapter.""" -from collections.abc import AsyncGenerator +from collections.abc import AsyncGenerator, Mapping, Sequence from datetime import UTC, datetime -from typing import Self, cast +from typing import Literal, Self, TypeAlias, cast, overload from uuid import UUID import weaviate @@ -10851,6 +12516,8 @@ from ..core.models import Document, DocumentMetadata, IngestionSource, StorageCo from ..utils.vectorizer import Vectorizer from .base import BaseStorage +VectorContainer: TypeAlias = Mapping[str, object] | Sequence[object] | None + class WeaviateStorage(BaseStorage): """Storage adapter for Weaviate.""" @@ -10935,22 +12602,39 @@ class WeaviateStorage(BaseStorage): raise StorageError(f"Failed to create collection: {e}") from e @staticmethod - def _extract_vector(vector_raw: object) -> list[float] | None: + def _extract_vector(vector_raw: VectorContainer) -> list[float] | None: """Normalize vector payloads returned by Weaviate into a float list.""" - if not isinstance(vector_raw, list) or not vector_raw: + if isinstance(vector_raw, Mapping): + default_vector = vector_raw.get("default") + return WeaviateStorage._extract_vector( + cast(VectorContainer, default_vector) + ) + + if not isinstance(vector_raw, Sequence) or isinstance( + vector_raw, (str, bytes, bytearray) + ): return None - if isinstance(vector_raw[0], (int, float)): + items = list(vector_raw) + if not items: + return None + + first_item = items[0] + if isinstance(first_item, (int, float)): + numeric_items = cast(list[int | float], items) try: - return [float(x) for x in vector_raw] + return [float(value) for value in numeric_items] except (TypeError, ValueError): return None - if isinstance(vector_raw[0], list): - inner = vector_raw[0] - if all(isinstance(item, (int, float)) for item in inner): + if isinstance(first_item, Sequence) and not isinstance( + first_item, (str, bytes, bytearray) + ): + inner_items = list(first_item) + if all(isinstance(item, (int, float)) for item in inner_items): try: - return [float(item) for item in inner] + numeric_inner = cast(list[int | float], inner_items) + return [float(item) for item in numeric_inner] except (TypeError, ValueError): return None @@ -10970,6 +12654,55 @@ class WeaviateStorage(BaseStorage): return IngestionSource.WEB + @staticmethod + @overload + def _coerce_properties( + properties: object, + *, + context: str, + ) -> Mapping[str, object]: + ... + + @staticmethod + @overload + def _coerce_properties( + properties: object, + *, + context: str, + allow_missing: Literal[False], + ) -> Mapping[str, object]: + ... + + @staticmethod + @overload + def _coerce_properties( + properties: object, + *, + context: str, + allow_missing: Literal[True], + ) -> Mapping[str, object] | None: + ... + + @staticmethod + def _coerce_properties( + properties: object, + *, + context: str, + allow_missing: bool = False, + ) -> Mapping[str, object] | None: + """Ensure Weaviate properties payloads are mappings.""" + if properties is None: + if allow_missing: + return None + raise StorageError(f"{context} returned object without properties") + + if not isinstance(properties, Mapping): + raise StorageError( + f"{context} returned invalid properties payload of type {type(properties)!r}" + ) + + return cast(Mapping[str, object], properties) + def _normalize_collection_name(self, collection_name: str | None) -> str: """Return a canonicalized collection name, defaulting to configured value.""" candidate = collection_name or self.config.collection_name @@ -11148,11 +12881,16 @@ class WeaviateStorage(BaseStorage): return None # Reconstruct document - props = result.properties + props = self._coerce_properties( + result.properties, + context="fetch_object_by_id", + ) metadata_dict = { "source_url": str(props["source_url"]), "title": str(props.get("title")) if props.get("title") else None, - "description": str(props.get("description")) if props.get("description") else None, + "description": str(props.get("description")) + if props.get("description") + else None, "timestamp": str(props["timestamp"]), "content_type": str(props["content_type"]), "word_count": int(str(props["word_count"])), @@ -11160,8 +12898,7 @@ class WeaviateStorage(BaseStorage): } metadata = cast(DocumentMetadata, cast(object, metadata_dict)) - vector_raw = result.vector.get("default") if result.vector else None - vector = self._extract_vector(vector_raw) + vector = self._extract_vector(cast(VectorContainer, result.vector)) return Document( id=UUID(document_id), @@ -11185,7 +12922,7 @@ class WeaviateStorage(BaseStorage): logging.warning(f"Unexpected error retrieving document {document_id}: {e}") return None - def _build_search_metadata(self, props: dict[str, object]) -> DocumentMetadata: + def _build_search_metadata(self, props: Mapping[str, object]) -> DocumentMetadata: """Build metadata dictionary from Weaviate properties.""" metadata_dict = { "source_url": str(props["source_url"]), @@ -11200,7 +12937,7 @@ class WeaviateStorage(BaseStorage): } return cast(DocumentMetadata, cast(object, metadata_dict)) - def _extract_search_score(self, result: DataObject) -> float | None: + def _extract_search_score(self, result: object) -> float | None: """Extract and convert search score from result metadata.""" metadata_obj = getattr(result, "metadata", None) if metadata_obj is None: @@ -11218,17 +12955,29 @@ class WeaviateStorage(BaseStorage): logging.debug(f"Invalid distance value {raw_distance}: {e}") return None - def _build_search_document(self, result: DataObject, resolved_name: str) -> Document: + def _build_search_document( + self, + result: object, + resolved_name: str, + ) -> Document: """Build Document from Weaviate search result.""" - props = result.properties + props = self._coerce_properties( + getattr(result, "properties", None), + context="search result", + ) metadata = self._build_search_metadata(props) - vector_raw = result.vector.get("default") if result.vector else None - vector = self._extract_vector(vector_raw) + vector_attr = getattr(result, "vector", None) + vector = self._extract_vector(cast(VectorContainer, vector_attr)) score_value = self._extract_search_score(result) + uuid_raw = getattr(result, "uuid", None) + if uuid_raw is None: + raise StorageError("Weaviate search result missing uuid") + uuid_value = uuid_raw if isinstance(uuid_raw, UUID) else UUID(str(uuid_raw)) + return Document( - id=result.uuid, + id=uuid_value, content=str(props["content"]), metadata=metadata, vector=vector, @@ -11335,13 +13084,13 @@ class WeaviateStorage(BaseStorage): except Exception as e: raise StorageError(f"Failed to list collections: {e}") from e - async def describe_collections(self) -> list[dict[str, str | int | float]]: + async def describe_collections(self) -> list[dict[str, object]]: """Return metadata for each Weaviate collection.""" if not self.client: raise StorageError("Weaviate client not initialized") try: - collections: list[dict[str, str | int | float]] = [] + collections: list[dict[str, object]] = [] for name in self.client.collections.list_all(): collection_obj = self.client.collections.get(name) if not collection_obj: @@ -11384,7 +13133,17 @@ class WeaviateStorage(BaseStorage): documents = [] for obj in response.objects: # Convert back to Document format - props = obj.properties + props = self._coerce_properties( + getattr(obj, "properties", None), + context="sample_documents", + allow_missing=True, + ) + if props is None: + continue + uuid_raw = getattr(obj, "uuid", None) + if uuid_raw is None: + continue + document_id = uuid_raw if isinstance(uuid_raw, UUID) else UUID(str(uuid_raw)) # Safely convert WeaviateField values word_count_val = props.get("word_count") if isinstance(word_count_val, (int, float)): @@ -11403,9 +13162,9 @@ class WeaviateStorage(BaseStorage): char_count = 0 doc = Document( - id=obj.uuid, + id=document_id, content=str(props.get("content", "")), - source=IngestionSource(str(props.get("source", "web"))), + source=self._parse_source(props.get("source")), metadata={ "source_url": str(props.get("source_url", "")), "title": str(props.get("title", "")) if props.get("title") else None, @@ -11437,7 +13196,7 @@ class WeaviateStorage(BaseStorage): else: return 0 - def _build_document_metadata(self, props: dict[str, object]) -> DocumentMetadata: + def _build_document_metadata(self, props: Mapping[str, object]) -> DocumentMetadata: """Build metadata from search document properties.""" return { "source_url": str(props.get("source_url", "")), @@ -11453,7 +13212,7 @@ class WeaviateStorage(BaseStorage): "char_count": self._safe_convert_count(props.get("char_count")), } - def _extract_document_score(self, obj: DataObject) -> float | None: + def _extract_document_score(self, obj: object) -> float | None: """Extract score from document search result.""" metadata_obj = getattr(obj, "metadata", None) if metadata_obj is None: @@ -11470,16 +13229,28 @@ class WeaviateStorage(BaseStorage): logging.debug(f"Invalid score value {raw_score}: {e}") return None - def _build_document_from_search(self, obj: DataObject, resolved_name: str) -> Document: + def _build_document_from_search( + self, + obj: object, + resolved_name: str, + ) -> Document: """Build Document from search document result.""" - props = obj.properties + props = self._coerce_properties( + getattr(obj, "properties", None), + context="document search result", + ) metadata = self._build_document_metadata(props) score_value = self._extract_document_score(obj) + uuid_raw = getattr(obj, "uuid", None) + if uuid_raw is None: + raise StorageError("Weaviate search document result missing uuid") + uuid_value = uuid_raw if isinstance(uuid_raw, UUID) else UUID(str(uuid_raw)) + return Document( - id=obj.uuid, + id=uuid_value, content=str(props.get("content", "")), - source=IngestionSource(str(props.get("source", "web"))), + source=self._parse_source(props.get("source")), metadata=metadata, collection=resolved_name, score=score_value, @@ -11530,7 +13301,7 @@ class WeaviateStorage(BaseStorage): offset: int = 0, *, collection_name: str | None = None, - ) -> list[dict[str, str | int]]: + ) -> list[dict[str, object]]: """ List documents in the collection with pagination. @@ -11552,9 +13323,15 @@ class WeaviateStorage(BaseStorage): limit=limit, offset=offset, return_metadata=["creation_time"] ) - documents = [] + documents: list[dict[str, object]] = [] for obj in response.objects: - props = obj.properties + props = self._coerce_properties( + obj.properties, + context="list_documents", + allow_missing=True, + ) + if props is None: + continue content = str(props.get("content", "")) word_count_value = props.get("word_count", 0) # Convert WeaviateField to int @@ -11565,7 +13342,7 @@ class WeaviateStorage(BaseStorage): else: word_count = 0 - doc_info: dict[str, str | int] = { + doc_info: dict[str, object] = { "id": str(obj.uuid), "title": str(props.get("title", "Untitled")), "source_url": str(props.get("source_url", "")), @@ -11725,15 +13502,6 @@ class WeaviateStorage(BaseStorage): pass # Ignore errors in destructor - -"""Utility modules.""" - -from .metadata_tagger import MetadataTagger -from .vectorizer import Vectorizer - -__all__ = ["MetadataTagger", "Vectorizer"] - - """Metadata tagger for enriching documents with AI-generated tags and metadata.""" @@ -11769,7 +13537,7 @@ class MetadataTagger: def __init__( self, llm_endpoint: str = "http://llm.lab", - model: str = "ollama/gpt-oss:20b", + model: str = "fireworks/glm-4p5-air", ): """ Initialize metadata tagger. @@ -11823,25 +13591,49 @@ class MetadataTagger: custom_instructions, ) - # Merge with existing metadata - preserve required fields + # Merge with existing metadata - preserve ALL existing fields and add LLM-generated ones + from typing import cast + from ..core.models import DocumentMetadata as CoreDocumentMetadata - updated_metadata: CoreDocumentMetadata = { - "source_url": document.metadata.get("source_url", ""), - "title": str(metadata.get("title", "")) or document.metadata.get("title"), - "description": metadata.get("summary") or document.metadata.get("description"), - "timestamp": document.metadata.get("timestamp", datetime.now(UTC)), - "content_type": document.metadata.get("content_type", "text/plain"), - "word_count": document.metadata.get("word_count", len(document.content.split())), - "char_count": document.metadata.get("char_count", len(document.content)), + # Start with a copy of existing metadata to preserve all fields + updated_metadata = dict(document.metadata) + + # Update/enhance with LLM-generated metadata, preserving existing values when new ones are empty + if metadata.get("title") and not updated_metadata.get("title"): + updated_metadata["title"] = str(metadata["title"]) + if metadata.get("summary") and not updated_metadata.get("description"): + updated_metadata["description"] = str(metadata["summary"]) + + # Ensure required fields have values + _ = updated_metadata.setdefault("source_url", "") + _ = updated_metadata.setdefault("timestamp", datetime.now(UTC)) + _ = updated_metadata.setdefault("content_type", "text/plain") + _ = updated_metadata.setdefault("word_count", len(document.content.split())) + _ = updated_metadata.setdefault("char_count", len(document.content)) + + # Build a proper DocumentMetadata instance with only valid keys + new_metadata: CoreDocumentMetadata = { + "source_url": str(updated_metadata.get("source_url", "")), + "timestamp": updated_metadata.get("timestamp", datetime.now(UTC)), + "content_type": str(updated_metadata.get("content_type", "text/plain")), + "word_count": (lambda wc: int(wc) if isinstance(wc, (int, str)) else 0)(updated_metadata.get("word_count", 0)), + "char_count": (lambda cc: int(cc) if isinstance(cc, (int, str)) else 0)(updated_metadata.get("char_count", 0)), } - # Store additional metadata as extra fields in the document's metadata - # Note: Since DocumentMetadata is a TypedDict, we can only include the defined fields - # Additional metadata like tags, category, etc. would need to be stored separately - # or the DocumentMetadata model would need to be extended + # Add optional fields if they exist + if "title" in updated_metadata and updated_metadata["title"]: + new_metadata["title"] = str(updated_metadata["title"]) + if "description" in updated_metadata and updated_metadata["description"]: + new_metadata["description"] = str(updated_metadata["description"]) + if "tags" in updated_metadata and isinstance(updated_metadata["tags"], list): + new_metadata["tags"] = [str(tag) for tag in updated_metadata["tags"]] + if "category" in updated_metadata and updated_metadata["category"]: + new_metadata["category"] = str(updated_metadata["category"]) + if "language" in updated_metadata and updated_metadata["language"]: + new_metadata["language"] = str(updated_metadata["language"]) - document.metadata = updated_metadata + document.metadata = new_metadata return document @@ -11931,23 +13723,23 @@ Return a JSON object with the following structure: if not isinstance(result_raw, dict): raise IngestionError("Invalid response format from LLM") - result = cast(dict[str, object], result_raw) + result = result_raw # Extract content from response choices = result.get("choices", []) if not choices or not isinstance(choices, list): raise IngestionError("No response from LLM") - first_choice_raw = cast(object, choices[0]) + first_choice_raw = choices[0] if not isinstance(first_choice_raw, dict): raise IngestionError("Invalid choice format") - first_choice = cast(dict[str, object], first_choice_raw) + first_choice = first_choice_raw message_raw = first_choice.get("message", {}) if not isinstance(message_raw, dict): raise IngestionError("Invalid message format") - message = cast(dict[str, object], message_raw) + message = message_raw content_str = str(message.get("content", "{}")) try: @@ -12078,7 +13870,7 @@ class Vectorizer: if api_key: headers["Authorization"] = f"Bearer {api_key}" - self.client = httpx.AsyncClient(timeout=60.0, headers=headers) # type: ignore[attr-defined] + self.client: httpx.AsyncClient = httpx.AsyncClient(timeout=60.0, headers=headers) async def vectorize(self, text: str) -> list[float]: """ @@ -12227,54 +14019,4 @@ class Vectorizer: await self.client.aclose() - -"""Main entry point for the ingestion pipeline.""" - -from .cli.main import app - -if __name__ == "__main__": - app() - - - -# API Keys -FIRECRAWL_API_KEY=fc-your-api-key -OPENWEBUI_API_KEY= -WEAVIATE_API_KEY= - -# Endpoints -LLM_ENDPOINT=http://llm.lab -WEAVIATE_ENDPOINT=http://weaviate.yo -OPENWEBUI_ENDPOINT=http://chat.lab - -# Model Configuration -EMBEDDING_MODEL=ollama/bge-m3:latest -EMBEDDING_DIMENSION=1024 - -# Ingestion Settings -BATCH_SIZE=50 -MAX_FILE_SIZE=1000000 -MAX_CRAWL_DEPTH=5 -MAX_CRAWL_PAGES=100 - -# Storage Settings -DEFAULT_STORAGE_BACKEND=weaviate -COLLECTION_PREFIX=docs - -# Prefect Settings -PREFECT_API_URL=http://prefect.lab -PREFECT_API_KEY=0nR4WAkQ3q9MY1bjqATK6pVmolighvrS -PREFECT_WORK_POOL=default - -# Scheduling -DEFAULT_SCHEDULE_INTERVAL=60 - -# Performance -MAX_CONCURRENT_TASKS=5 -REQUEST_TIMEOUT=60 - -# Logging -LOG_LEVEL=INFO - - diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__pycache__/__init__.cpython-312.pyc b/tests/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..72ead4f Binary files /dev/null and b/tests/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc b/tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..e4ecd5a Binary files /dev/null and b/tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/__pycache__/conftest.cpython-312.pyc b/tests/__pycache__/conftest.cpython-312.pyc new file mode 100644 index 0000000..5496795 Binary files /dev/null and b/tests/__pycache__/conftest.cpython-312.pyc differ diff --git a/tests/__pycache__/openapi_mocks.cpython-312.pyc b/tests/__pycache__/openapi_mocks.cpython-312.pyc new file mode 100644 index 0000000..b439d58 Binary files /dev/null and b/tests/__pycache__/openapi_mocks.cpython-312.pyc differ diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..70cef40 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,545 @@ +from __future__ import annotations + +from collections import deque +from collections.abc import Mapping +from dataclasses import dataclass, field +from datetime import UTC, datetime +from pathlib import Path +from types import SimpleNamespace +from typing import Protocol, TypedDict, cast +from urllib.parse import urlparse + +import httpx +import pytest +from pydantic import HttpUrl + +from ingest_pipeline.core.models import ( + Document, + DocumentMetadata, + IngestionSource, + StorageBackend, + StorageConfig, + VectorConfig, +) +from typings import EmbeddingData, EmbeddingResponse + +from .openapi_mocks import ( + FirecrawlMockService, + OpenAPISpec, + OpenWebUIMockService, + R2RMockService, +) + +# Type aliases for mock responses +MockResponseData = dict[str, object] + +PROJECT_ROOT = Path(__file__).resolve().parent.parent + + +class RequestRecord(TypedDict): + """Captured HTTP request payload.""" + + method: str + url: str + json_body: dict[str, object] | None + params: dict[str, object] | None + files: object | None + + +@dataclass(slots=True) +class StubbedResponse: + """In-memory HTTP response for httpx client mocking.""" + + payload: object + status_code: int = 200 + + def json(self) -> object: + return self.payload + + def raise_for_status(self) -> None: + if self.status_code < 400: + return + # Create a minimal exception for testing - we don't need full httpx objects for tests + class TestHTTPError(Exception): + request: object | None + response: object | None + + def __init__(self, message: str) -> None: + super().__init__(message) + self.request = None + self.response = None + + raise TestHTTPError(f"Stubbed HTTP error with status {self.status_code}") + + +@dataclass(slots=True) +class AsyncClientStub: + """Replacement for httpx.AsyncClient used in tests.""" + + responses: deque[StubbedResponse] + requests: list[RequestRecord] + owner: HttpxStub + timeout: object | None = None + headers: dict[str, str] = field(default_factory=dict) + base_url: str = "" + + def __init__( + self, + *, + responses: deque[StubbedResponse], + requests: list[RequestRecord], + timeout: object | None = None, + headers: dict[str, str] | None = None, + base_url: str | None = None, + owner: HttpxStub, + **_: object, + ) -> None: + self.responses = responses + self.requests = requests + self.timeout = timeout + self.headers = dict(headers or {}) + self.base_url = str(base_url or "") + self.owner = owner + + def _normalize_url(self, url: str) -> str: + if url.startswith("http://") or url.startswith("https://"): + return url + if not self.base_url: + return url + prefix = self.base_url.rstrip("/") + suffix = url.lstrip("/") + return f"{prefix}/{suffix}" if suffix else prefix + + def _consume( + self, + *, + method: str, + url: str, + json: dict[str, object] | None, + params: dict[str, object] | None, + files: object | None, + ) -> StubbedResponse: + if self.responses: + return self.responses.popleft() + dispatched = self.owner.dispatch( + method=method, + url=url, + json=json, + params=params, + files=files, + ) + if dispatched is not None: + return dispatched + raise AssertionError(f"No stubbed response for {method} {url}") + + def _record( + self, + *, + method: str, + url: str, + json: dict[str, object] | None, + params: dict[str, object] | None, + files: object | None, + ) -> str: + normalized = self._normalize_url(url) + record: RequestRecord = { + "method": method, + "url": normalized, + "json_body": json, + "params": params, + "files": files, + } + self.requests.append(record) + return normalized + + async def post( + self, + url: str, + *, + json: dict[str, object] | None = None, + files: object | None = None, + params: dict[str, object] | None = None, + ) -> StubbedResponse: + normalized = self._record( + method="POST", + url=url, + json=json, + params=params, + files=files, + ) + return self._consume( + method="POST", + url=normalized, + json=json, + params=params, + files=files, + ) + + async def get( + self, + url: str, + *, + params: dict[str, object] | None = None, + ) -> StubbedResponse: + normalized = self._record( + method="GET", + url=url, + json=None, + params=params, + files=None, + ) + return self._consume( + method="GET", + url=normalized, + json=None, + params=params, + files=None, + ) + + async def delete( + self, + url: str, + *, + params: dict[str, object] | None = None, + json: dict[str, object] | None = None, + ) -> StubbedResponse: + normalized = self._record( + method="DELETE", + url=url, + json=json, + params=params, + files=None, + ) + return self._consume( + method="DELETE", + url=normalized, + json=json, + params=params, + files=None, + ) + + async def aclose(self) -> None: + return None + + async def __aenter__(self) -> AsyncClientStub: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: object | None, + ) -> None: + return None + + +@dataclass(slots=True) +class HttpxStub: + """Helper exposing queued responses and captured requests.""" + + responses: deque[StubbedResponse] = field(default_factory=deque) + requests: list[RequestRecord] = field(default_factory=list) + clients: list[AsyncClientStub] = field(default_factory=list) + services: dict[str, MockService] = field(default_factory=dict) + + def queue_json(self, payload: object, status_code: int = 200) -> None: + self.responses.append(StubbedResponse(payload=payload, status_code=status_code)) + + def register_service(self, base_url: str, service: MockService) -> None: + normalized = base_url.rstrip("/") or base_url + self.services[normalized] = service + + def dispatch( + self, + *, + method: str, + url: str, + json: Mapping[str, object] | None, + params: Mapping[str, object] | None, + files: object | None, + ) -> StubbedResponse | None: + parsed = urlparse(url) + base = f"{parsed.scheme}://{parsed.netloc}" if parsed.scheme and parsed.netloc else "" + base = base.rstrip("/") if base else base + path = parsed.path or "/" + service = self.services.get(base) + if service is None: + return None + status, payload = service.handle( + method=method, + path=path, + json=json, + params=params, + files=files, + ) + return StubbedResponse(payload=payload, status_code=status) + + +class DocumentFactory(Protocol): + """Callable protocol for building Document instances.""" + + def __call__( + self, + *, + content: str, + metadata_updates: dict[str, object] | None = None, + ) -> Document: + """Create Document for testing.""" + ... + + +class VectorConfigFactory(Protocol): + """Callable protocol for building VectorConfig instances.""" + + def __call__(self, *, model: str, dimension: int, endpoint: str) -> VectorConfig: + """Create VectorConfig for testing.""" + ... + + +class EmbeddingPayloadFactory(Protocol): + """Callable protocol for synthesizing embedding responses.""" + + def __call__(self, *, dimension: int) -> EmbeddingResponse: + """Create embedding payload with the requested dimension.""" + ... + + +class MockService(Protocol): + """Protocol for mock services that can handle HTTP requests.""" + + def handle( + self, + *, + method: str, + path: str, + json: Mapping[str, object] | None, + params: Mapping[str, object] | None, + files: object | None, + ) -> tuple[int, object]: + """Handle HTTP request and return status code and response payload.""" + ... + + +@pytest.fixture(scope="session") +def base_metadata() -> DocumentMetadata: + """Provide reusable base metadata for document fixtures.""" + + required = { + "source_url": "https://example.com/article", + "timestamp": datetime.now(UTC), + "content_type": "text/plain", + "word_count": 100, + "char_count": 500, + } + return cast(DocumentMetadata, cast(object, required)) + + +@pytest.fixture(scope="module") +def document_factory(base_metadata: DocumentMetadata) -> DocumentFactory: + """Build Document models with deterministic defaults.""" + + def _factory( + *, + content: str, + metadata_updates: dict[str, object] | None = None, + ) -> Document: + metadata_dict = dict(base_metadata) + if metadata_updates: + metadata_dict.update(metadata_updates) + return Document( + content=content, + metadata=cast(DocumentMetadata, cast(object, metadata_dict)), + source=IngestionSource.WEB, + ) + + return _factory + + +@pytest.fixture(scope="session") +def vector_config_factory() -> VectorConfigFactory: + """Construct VectorConfig instances for tests.""" + + def _factory(*, model: str, dimension: int, endpoint: str) -> VectorConfig: + return VectorConfig(model=model, dimension=dimension, embedding_endpoint=HttpUrl(endpoint)) + + return _factory + + +@pytest.fixture(scope="session") +def storage_config() -> StorageConfig: + """Provide canonical storage configuration for adapters.""" + + return StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint=HttpUrl("http://storage.local"), + collection_name="documents", + ) + + +@pytest.fixture(scope="session") +def r2r_storage_config() -> StorageConfig: + """Provide storage configuration for R2R adapter tests.""" + + return StorageConfig( + backend=StorageBackend.R2R, + endpoint=HttpUrl("http://r2r.local"), + collection_name="documents", + ) + + +@pytest.fixture(scope="session") +def openwebui_spec() -> OpenAPISpec: + """Load OpenWebUI OpenAPI specification.""" + + return OpenAPISpec.from_file(PROJECT_ROOT / "chat.json") + + +@pytest.fixture(scope="session") +def r2r_spec() -> OpenAPISpec: + """Load R2R OpenAPI specification.""" + + return OpenAPISpec.from_file(PROJECT_ROOT / "r2r.json") + + + +@pytest.fixture(scope="session") +def firecrawl_spec() -> OpenAPISpec: + """Load Firecrawl OpenAPI specification.""" + + return OpenAPISpec.from_file(PROJECT_ROOT / "firecrawl.json") + + +@pytest.fixture(scope="function") +def httpx_stub(monkeypatch: pytest.MonkeyPatch) -> HttpxStub: + """Replace httpx.AsyncClient with an in-memory stub.""" + + stub = HttpxStub() + + def _client_factory(**kwargs: object) -> AsyncClientStub: + client = AsyncClientStub( + responses=stub.responses, + requests=stub.requests, + timeout=kwargs.get("timeout"), + headers=cast(dict[str, str] | None, kwargs.get("headers")), + base_url=cast(str | None, kwargs.get("base_url")), + owner=stub, + ) + stub.clients.append(client) + return client + + monkeypatch.setattr(httpx, "AsyncClient", _client_factory) + monkeypatch.delenv("LLM_API_KEY", raising=False) + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + return stub + + +@pytest.fixture(scope="function") +def openwebui_service( + httpx_stub: HttpxStub, + openwebui_spec: OpenAPISpec, + storage_config: StorageConfig, +) -> OpenWebUIMockService: + """Stateful mock for OpenWebUI APIs.""" + + service = OpenWebUIMockService( + base_url=str(storage_config.endpoint), + spec=openwebui_spec, + ) + httpx_stub.register_service(service.base_url, service) + return service + + +@pytest.fixture(scope="function") +def r2r_service( + httpx_stub: HttpxStub, + r2r_spec: OpenAPISpec, + r2r_storage_config: StorageConfig, +) -> R2RMockService: + """Stateful mock for R2R APIs.""" + + service = R2RMockService( + base_url=str(r2r_storage_config.endpoint), + spec=r2r_spec, + ) + httpx_stub.register_service(service.base_url, service) + return service + + +@pytest.fixture(scope="function") +def firecrawl_service( + httpx_stub: HttpxStub, + firecrawl_spec: OpenAPISpec, +) -> FirecrawlMockService: + """Stateful mock for Firecrawl APIs.""" + + service = FirecrawlMockService( + base_url="http://crawl.lab:30002", + spec=firecrawl_spec, + ) + httpx_stub.register_service(service.base_url, service) + return service + + +@pytest.fixture(scope="function") +def firecrawl_client_stub( + monkeypatch: pytest.MonkeyPatch, + firecrawl_service: FirecrawlMockService, +) -> object: + """Patch AsyncFirecrawl to use the mock service.""" + + class AsyncFirecrawlStub: + _service: FirecrawlMockService + + def __init__(self, *args: object, **kwargs: object) -> None: + self._service = firecrawl_service + + async def map(self, url: str, limit: int | None = None, **_: object) -> SimpleNamespace: + payload = cast(MockResponseData, self._service.map_response(url, limit)) + links_data = cast(list[MockResponseData], payload.get("links", [])) + links = [SimpleNamespace(url=cast(str, item.get("url", ""))) for item in links_data] + return SimpleNamespace(success=payload.get("success", True), links=links) + + async def scrape(self, url: str, formats: list[str] | None = None, **_: object) -> SimpleNamespace: + payload = cast(MockResponseData, self._service.scrape_response(url, formats)) + data = cast(MockResponseData, payload.get("data", {})) + metadata_payload = cast(MockResponseData, data.get("metadata", {})) + metadata_obj = SimpleNamespace(**metadata_payload) + return SimpleNamespace( + markdown=data.get("markdown"), + html=data.get("html"), + rawHtml=data.get("rawHtml"), + links=data.get("links", []), + metadata=metadata_obj, + ) + + async def close(self) -> None: + return None + + async def __aenter__(self) -> AsyncFirecrawlStub: + return self + + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: object | None, + ) -> None: + return None + + monkeypatch.setattr( + "ingest_pipeline.ingestors.firecrawl.AsyncFirecrawl", + AsyncFirecrawlStub, + ) + return AsyncFirecrawlStub + + +@pytest.fixture(scope="function") +def embedding_payload_factory() -> EmbeddingPayloadFactory: + """Provide embedding payloads tailored to the requested dimension.""" + + def _factory(*, dimension: int) -> EmbeddingResponse: + vector = [float(index) for index in range(dimension)] + data_entry: EmbeddingData = {"embedding": vector} + return {"data": [data_entry]} + + return _factory diff --git a/tests/openapi_mocks.py b/tests/openapi_mocks.py new file mode 100644 index 0000000..9f4d22e --- /dev/null +++ b/tests/openapi_mocks.py @@ -0,0 +1,807 @@ +from __future__ import annotations + +import copy +import json as json_module +import time +from collections.abc import Mapping +from datetime import UTC, datetime +from pathlib import Path +from typing import Any +from uuid import uuid4 + + +class OpenAPISpec: + """Utility for generating example payloads from an OpenAPI document.""" + + def __init__(self, raw: Mapping[str, Any]): + self._raw = raw + self._paths = raw.get("paths", {}) + self._components = raw.get("components", {}) + + @classmethod + def from_file(cls, path: Path) -> OpenAPISpec: + with path.open("r", encoding="utf-8") as handle: + raw = json_module.load(handle) + return cls(raw) + + def resolve_ref(self, ref: str) -> Mapping[str, Any]: + if not ref.startswith("#/"): + raise ValueError(f"Unsupported $ref format: {ref}") + parts = ref.lstrip("#/").split("/") + node: Any = self._raw + for part in parts: + if not isinstance(node, Mapping) or part not in node: + raise KeyError(f"Unable to resolve reference: {ref}") + node = node[part] + if not isinstance(node, Mapping): + raise TypeError(f"Reference {ref} did not resolve to an object") + return node + + def generate(self, schema: Mapping[str, Any] | None, name: str = "value") -> Any: + if schema is None: + return None + if "$ref" in schema: + resolved = self.resolve_ref(schema["$ref"]) + return self.generate(resolved, name=name) + if "example" in schema: + return copy.deepcopy(schema["example"]) + if "default" in schema and schema["default"] is not None: + return copy.deepcopy(schema["default"]) + if "enum" in schema: + enum_values = schema["enum"] + if enum_values: + return copy.deepcopy(enum_values[0]) + if "anyOf" in schema: + for option in schema["anyOf"]: + candidate = self.generate(option, name=name) + if candidate is not None: + return candidate + if "oneOf" in schema: + return self.generate(schema["oneOf"][0], name=name) + if "allOf" in schema: + result: dict[str, Any] = {} + for option in schema["allOf"]: + fragment = self.generate(option, name=name) + if isinstance(fragment, Mapping): + result.update(fragment) + return result + + type_name = schema.get("type") + if type_name == "object": + properties = schema.get("properties", {}) + required = schema.get("required", []) + result: dict[str, Any] = {} + keys = list(properties.keys()) or list(required) + for key in keys: + prop_schema = properties.get(key, {}) + result[key] = self.generate(prop_schema, name=key) + additional = schema.get("additionalProperties") + if additional and isinstance(additional, Mapping) and not properties: + result["key"] = self.generate(additional, name="key") + return result + if type_name == "array": + item_schema = schema.get("items", {}) + item = self.generate(item_schema, name=name) + return [] if item is None else [item] + if type_name == "string": + format_hint = schema.get("format") + if format_hint == "date-time": + return "2024-01-01T00:00:00+00:00" + if format_hint == "date": + return "2024-01-01" + if format_hint == "uuid": + return "00000000-0000-4000-8000-000000000000" + if format_hint == "uri": + return "https://example.com" + return f"{name}-value" + if type_name == "integer": + minimum = schema.get("minimum") + if minimum is not None: + return int(minimum) + return 1 + if type_name == "number": + return 1.0 + if type_name == "boolean": + return True + if type_name == "null": + return None + return {} + + def _split_path(self, path: str) -> list[str]: + if path in {"", "/"}: + return [] + return [segment for segment in path.strip("/").split("/") if segment] + + def find_operation(self, method: str, path: str) -> tuple[Mapping[str, Any] | None, dict[str, str]]: + method = method.lower() + normalized = "/" if path in {"", "/"} else "/" + path.strip("/") + actual_segments = self._split_path(normalized) + for template, operations in self._paths.items(): + operation = operations.get(method) + if operation is None: + continue + template_path = template if template else "/" + template_segments = self._split_path(template_path) + if len(template_segments) != len(actual_segments): + continue + params: dict[str, str] = {} + matched = True + for template_part, actual_part in zip(template_segments, actual_segments, strict=False): + if template_part.startswith("{") and template_part.endswith("}"): + params[template_part[1:-1]] = actual_part + elif template_part != actual_part: + matched = False + break + if matched: + return operation, params + return None, {} + + def build_response( + self, + operation: Mapping[str, Any], + status: str | None = None, + ) -> tuple[int, Any]: + responses = operation.get("responses", {}) + target_status = status or ( + "200" if "200" in responses else next(iter(responses), "200") + ) + status_code = 200 + try: + status_code = int(target_status) + except ValueError: + pass + response_entry = responses.get(target_status, {}) + content = response_entry.get("content", {}) + media = None + for candidate in ("application/json", "application/problem+json", "text/plain"): + if candidate in content: + media = content[candidate] + break + schema = media.get("schema") if media else None + payload = self.generate(schema, name="response") + return status_code, payload + + def generate_from_ref( + self, + ref: str, + overrides: Mapping[str, Any] | None = None, + ) -> Any: + base = self.generate({"$ref": ref}) + if overrides is None or not isinstance(base, Mapping): + return copy.deepcopy(base) + merged = copy.deepcopy(base) + for key, value in overrides.items(): + if isinstance(value, Mapping) and isinstance(merged.get(key), Mapping): + merged[key] = self.generate_from_mapping( + merged[key], + value, + ) + else: + merged[key] = copy.deepcopy(value) + return merged + + def generate_from_mapping( + self, + base: Mapping[str, Any], + overrides: Mapping[str, Any], + ) -> Mapping[str, Any]: + result = copy.deepcopy(base) + for key, value in overrides.items(): + if isinstance(value, Mapping) and isinstance(result.get(key), Mapping): + result[key] = self.generate_from_mapping(result[key], value) + else: + result[key] = copy.deepcopy(value) + return result + + +class OpenAPIMockService: + """Base class for stateful mock services backed by an OpenAPI spec.""" + + def __init__(self, base_url: str, spec: OpenAPISpec): + self.base_url = base_url.rstrip("/") + self.spec = spec + + @staticmethod + def _normalize_path(path: str) -> str: + if not path or path == "/": + return "/" + return "/" + path.strip("/") + + def handle( + self, + *, + method: str, + path: str, + json: Mapping[str, Any] | None, + params: Mapping[str, Any] | None, + files: Any, + ) -> tuple[int, Any]: + operation, _ = self.spec.find_operation(method, path) + if operation is None: + return 404, {"detail": f"Unhandled {method.upper()} {path}"} + return self.spec.build_response(operation) + + +class OpenWebUIMockService(OpenAPIMockService): + """Stateful mock capturing OpenWebUI knowledge and file operations.""" + + def __init__(self, base_url: str, spec: OpenAPISpec): + super().__init__(base_url, spec) + self._knowledge: dict[str, dict[str, Any]] = {} + self._files: dict[str, dict[str, Any]] = {} + self._knowledge_counter = 1 + self._file_counter = 1 + self._default_user = "user-1" + + @staticmethod + def _timestamp() -> int: + return int(time.time()) + + def ensure_knowledge( + self, + *, + name: str, + description: str = "", + knowledge_id: str | None = None, + ) -> dict[str, Any]: + identifier = knowledge_id or f"kb-{self._knowledge_counter}" + self._knowledge_counter += 1 + entry = self.spec.generate_from_ref("#/components/schemas/KnowledgeUserResponse") + ts = self._timestamp() + entry.update( + { + "id": identifier, + "user_id": self._default_user, + "name": name, + "description": description or entry.get("description") or "", + "created_at": ts, + "updated_at": ts, + "data": entry.get("data") or {}, + "meta": entry.get("meta") or {}, + "access_control": entry.get("access_control") or {}, + "files": [], + } + ) + self._knowledge[identifier] = entry + return copy.deepcopy(entry) + + def create_file( + self, + *, + filename: str, + user_id: str | None = None, + file_id: str | None = None, + ) -> dict[str, Any]: + identifier = file_id or f"file-{self._file_counter}" + self._file_counter += 1 + entry = self.spec.generate_from_ref("#/components/schemas/FileModelResponse") + ts = self._timestamp() + entry.update( + { + "id": identifier, + "user_id": user_id or self._default_user, + "filename": filename, + "meta": entry.get("meta") or {}, + "created_at": ts, + "updated_at": ts, + } + ) + self._files[identifier] = entry + return copy.deepcopy(entry) + + def _build_file_metadata(self, file_id: str) -> dict[str, Any]: + metadata = self.spec.generate_from_ref("#/components/schemas/FileMetadataResponse") + source = self._files.get(file_id) + ts = self._timestamp() + metadata.update( + { + "id": file_id, + "meta": (source or {}).get("meta", {}), + "created_at": ts, + "updated_at": ts, + } + ) + return metadata + + def get_knowledge(self, knowledge_id: str) -> dict[str, Any] | None: + entry = self._knowledge.get(knowledge_id) + return copy.deepcopy(entry) if entry is not None else None + + def find_knowledge_by_name(self, name: str) -> tuple[str, dict[str, Any]] | None: + for identifier, entry in self._knowledge.items(): + if entry.get("name") == name: + return identifier, copy.deepcopy(entry) + return None + + def attach_existing_file(self, knowledge_id: str, file_id: str) -> None: + if knowledge_id not in self._knowledge: + raise KeyError(f"Knowledge {knowledge_id} not found") + knowledge = self._knowledge[knowledge_id] + metadata = self._build_file_metadata(file_id) + knowledge.setdefault("files", []) + knowledge["files"] = [item for item in knowledge["files"] if item.get("id") != file_id] + knowledge["files"].append(metadata) + knowledge["updated_at"] = self._timestamp() + + def _knowledge_user_response(self, knowledge: Mapping[str, Any]) -> dict[str, Any]: + payload = self.spec.generate_from_ref("#/components/schemas/KnowledgeUserResponse") + payload.update({ + "id": knowledge["id"], + "user_id": knowledge["user_id"], + "name": knowledge["name"], + "description": knowledge.get("description", ""), + "data": copy.deepcopy(knowledge.get("data", {})), + "meta": copy.deepcopy(knowledge.get("meta", {})), + "access_control": copy.deepcopy(knowledge.get("access_control", {})), + "created_at": knowledge.get("created_at", self._timestamp()), + "updated_at": knowledge.get("updated_at", self._timestamp()), + "files": copy.deepcopy(knowledge.get("files", [])), + }) + return payload + + def _knowledge_files_response(self, knowledge: Mapping[str, Any]) -> dict[str, Any]: + payload = self.spec.generate_from_ref("#/components/schemas/KnowledgeFilesResponse") + payload.update( + { + "id": knowledge["id"], + "user_id": knowledge["user_id"], + "name": knowledge["name"], + "description": knowledge.get("description", ""), + "data": copy.deepcopy(knowledge.get("data", {})), + "meta": copy.deepcopy(knowledge.get("meta", {})), + "access_control": copy.deepcopy(knowledge.get("access_control", {})), + "created_at": knowledge.get("created_at", self._timestamp()), + "updated_at": knowledge.get("updated_at", self._timestamp()), + "files": copy.deepcopy(knowledge.get("files", [])), + } + ) + return payload + + def handle( + self, + *, + method: str, + path: str, + json: Mapping[str, Any] | None, + params: Mapping[str, Any] | None, + files: Any, + ) -> tuple[int, Any]: + normalized = self._normalize_path(path) + segments = [segment for segment in normalized.strip("/").split("/") if segment] + if segments[:3] != ["api", "v1", "knowledge"]: + return super().handle(method=method, path=path, json=json, params=params, files=files) + + method_upper = method.upper() + segment_count = len(segments) + + if method_upper == "GET" and segment_count == 4 and segments[3] == "list": + body = [self._knowledge_user_response(entry) for entry in self._knowledge.values()] + return 200, body + + if method_upper == "GET" and segment_count == 3: + body = [self._knowledge_user_response(entry) for entry in self._knowledge.values()] + return 200, body + + if method_upper == "POST" and segment_count == 4 and segments[3] == "create": + payload = json or {} + entry = self.ensure_knowledge( + name=str(payload.get("name", "knowledge")), + description=str(payload.get("description", "")), + ) + return 200, entry + + if segment_count >= 4: + knowledge_id = segments[3] + knowledge = self._knowledge.get(knowledge_id) + if knowledge is None: + return 404, {"detail": f"Knowledge {knowledge_id} not found"} + + if method_upper == "GET" and segment_count == 4: + return 200, self._knowledge_files_response(knowledge) + + if method_upper == "POST" and segment_count == 6 and segments[4:] == ["file", "add"]: + payload = json or {} + file_id = str(payload.get("file_id", "")) + if not file_id: + return 422, {"detail": "file_id is required"} + if file_id not in self._files: + self.create_file(filename=f"{file_id}.txt") + metadata = self._build_file_metadata(file_id) + knowledge.setdefault("files", []) + knowledge["files"] = [item for item in knowledge["files"] if item.get("id") != file_id] + knowledge["files"].append(metadata) + knowledge["updated_at"] = self._timestamp() + return 200, self._knowledge_files_response(knowledge) + + if method_upper == "POST" and segment_count == 6 and segments[4:] == ["file", "remove"]: + payload = json or {} + file_id = str(payload.get("file_id", "")) + knowledge["files"] = [item for item in knowledge.get("files", []) if item.get("id") != file_id] + knowledge["updated_at"] = self._timestamp() + return 200, self._knowledge_files_response(knowledge) + + if method_upper == "DELETE" and segment_count == 5 and segments[4] == "delete": + self._knowledge.pop(knowledge_id, None) + return 200, True + + if method_upper == "POST" and segments == ["api", "v1", "files"]: + filename = "uploaded.txt" + if files and isinstance(files, Mapping): + file_entry = files.get("file") + if isinstance(file_entry, tuple) and len(file_entry) >= 1: + filename = str(file_entry[0]) or filename + entry = self.create_file(filename=filename) + return 200, entry + + if method_upper == "DELETE" and segments[:3] == ["api", "v1", "files"] and len(segments) == 4: + file_id = segments[3] + self._files.pop(file_id, None) + for knowledge in self._knowledge.values(): + knowledge["files"] = [item for item in knowledge.get("files", []) if item.get("id") != file_id] + return 200, {"deleted": True} + + return super().handle(method=method, path=path, json=json, params=params, files=files) + + +class R2RMockService(OpenAPIMockService): + """Stateful mock of core R2R collection endpoints.""" + + def __init__(self, base_url: str, spec: OpenAPISpec): + super().__init__(base_url, spec) + self._collections: dict[str, dict[str, Any]] = {} + self._documents: dict[str, dict[str, Any]] = {} + + @staticmethod + def _iso_now() -> str: + return datetime.now(tz=UTC).isoformat() + + def create_collection( + self, + *, + name: str, + description: str = "", + collection_id: str | None = None, + ) -> dict[str, Any]: + identifier = collection_id or str(uuid4()) + entry = self.spec.generate_from_ref("#/components/schemas/CollectionResponse") + timestamp = self._iso_now() + entry.update( + { + "id": identifier, + "owner_id": entry.get("owner_id") or str(uuid4()), + "name": name, + "description": description or entry.get("description") or "", + "graph_cluster_status": entry.get("graph_cluster_status") or "idle", + "graph_sync_status": entry.get("graph_sync_status") or "synced", + "created_at": timestamp, + "updated_at": timestamp, + "user_count": entry.get("user_count", 1) or 1, + "document_count": entry.get("document_count", 0) or 0, + "documents": entry.get("documents", []), + } + ) + self._collections[identifier] = entry + return copy.deepcopy(entry) + + def get_collection(self, collection_id: str) -> dict[str, Any] | None: + entry = self._collections.get(collection_id) + return copy.deepcopy(entry) if entry is not None else None + + def find_collection_by_name(self, name: str) -> tuple[str, dict[str, Any]] | None: + for identifier, entry in self._collections.items(): + if entry.get("name") == name: + return identifier, copy.deepcopy(entry) + return None + + def _set_collection_document_ids(self, collection_id: str, document_id: str, *, add: bool) -> None: + collection = self._collections.get(collection_id) + if collection is None: + collection = self.create_collection(name=f"Collection {collection_id}", collection_id=collection_id) + documents = collection.setdefault("documents", []) + if add: + if document_id not in documents: + documents.append(document_id) + else: + documents[:] = [doc for doc in documents if doc != document_id] + collection["document_count"] = len(documents) + + def create_document( + self, + *, + document_id: str, + content: str, + metadata: dict[str, Any], + collection_ids: list[str], + ) -> dict[str, Any]: + entry = self.spec.generate_from_ref("#/components/schemas/DocumentResponse") + timestamp = self._iso_now() + entry.update( + { + "id": document_id, + "owner_id": entry.get("owner_id") or str(uuid4()), + "collection_ids": collection_ids, + "metadata": copy.deepcopy(metadata), + "document_type": entry.get("document_type") or "text", + "version": entry.get("version") or "1.0", + "size_in_bytes": metadata.get("char_count") or len(content.encode("utf-8")), + "created_at": entry.get("created_at") or timestamp, + "updated_at": timestamp, + "summary": entry.get("summary"), + "summary_embedding": entry.get("summary_embedding") or None, + "chunks": entry.get("chunks") or [], + "content": content, + } + ) + entry["__content"] = content + self._documents[document_id] = entry + for collection_id in collection_ids: + self._set_collection_document_ids(collection_id, document_id, add=True) + return copy.deepcopy(entry) + + def get_document(self, document_id: str) -> dict[str, Any] | None: + entry = self._documents.get(document_id) + if entry is None: + return None + return copy.deepcopy(entry) + + def delete_document(self, document_id: str) -> bool: + entry = self._documents.pop(document_id, None) + if entry is None: + return False + for collection_id in entry.get("collection_ids", []): + self._set_collection_document_ids(collection_id, document_id, add=False) + return True + + def append_document_metadata(self, document_id: str, metadata_list: list[dict[str, Any]]) -> dict[str, Any] | None: + entry = self._documents.get(document_id) + if entry is None: + return None + metadata = entry.setdefault("metadata", {}) + for item in metadata_list: + for key, value in item.items(): + metadata[key] = value + entry["updated_at"] = self._iso_now() + return copy.deepcopy(entry) + + def handle( + self, + *, + method: str, + path: str, + json: Mapping[str, Any] | None, + params: Mapping[str, Any] | None, + files: Any, + ) -> tuple[int, Any]: + normalized = self._normalize_path(path) + method_upper = method.upper() + + if normalized == "/v3/collections" and method_upper == "GET": + payload = self.spec.generate_from_ref( + "#/components/schemas/PaginatedR2RResult_list_CollectionResponse__" + ) + results = [] + for _identifier, entry in self._collections.items(): + clone = copy.deepcopy(entry) + clone["document_count"] = len(clone.get("documents", [])) + results.append(clone) + payload.update( + { + "results": results, + "total_entries": len(results), + } + ) + return 200, payload + + if normalized == "/v3/collections" and method_upper == "POST": + body = json or {} + entry = self.create_collection( + name=str(body.get("name", "Collection")), + description=str(body.get("description", "")), + ) + payload = self.spec.generate_from_ref("#/components/schemas/R2RResults_CollectionResponse_") + payload.update({"results": entry}) + return 200, payload + + segments = [segment for segment in normalized.strip("/").split("/") if segment] + if segments[:2] == ["v3", "documents"]: + if method_upper == "POST" and len(segments) == 2: + metadata_raw = {} + content = "" + doc_id = str(uuid4()) + collection_ids: list[str] = [] + if isinstance(files, Mapping): + if "metadata" in files: + metadata_entry = files["metadata"] + if isinstance(metadata_entry, tuple) and len(metadata_entry) >= 2: + try: + metadata_raw = json_module.loads(metadata_entry[1] or "{}") + except json_module.JSONDecodeError: + metadata_raw = {} + if "raw_text" in files: + raw_text_entry = files["raw_text"] + if isinstance(raw_text_entry, tuple) and len(raw_text_entry) >= 2 and raw_text_entry[1] is not None: + content = str(raw_text_entry[1]) + if "id" in files: + id_entry = files["id"] + if isinstance(id_entry, tuple) and len(id_entry) >= 2 and id_entry[1]: + doc_id = str(id_entry[1]) + if "collection_ids" in files: + coll_entry = files["collection_ids"] + if isinstance(coll_entry, tuple) and len(coll_entry) >= 2 and coll_entry[1]: + try: + parsed = json_module.loads(coll_entry[1]) + if isinstance(parsed, list): + collection_ids = [str(item) for item in parsed] + except json_module.JSONDecodeError: + collection_ids = [] + if not collection_ids: + name = metadata_raw.get("collection_name") or metadata_raw.get("collection") + if isinstance(name, str): + located = self.find_collection_by_name(name) + if located: + collection_ids = [located[0]] + if not collection_ids and self._collections: + collection_ids = [next(iter(self._collections))] + document = self.create_document( + document_id=doc_id, + content=content, + metadata=metadata_raw, + collection_ids=collection_ids, + ) + response_payload = self.spec.generate_from_ref( + "#/components/schemas/R2RResults_IngestionResponse_" + ) + ingestion = self.spec.generate_from_ref("#/components/schemas/IngestionResponse") + ingestion.update( + { + "message": ingestion.get("message") or "Ingestion task queued successfully.", + "document_id": document["id"], + "task_id": ingestion.get("task_id") or str(uuid4()), + } + ) + response_payload["results"] = ingestion + return 202, response_payload + + if method_upper == "GET" and len(segments) == 3: + doc_id = segments[2] + document = self.get_document(doc_id) + if document is None: + return 404, {"detail": f"Document {doc_id} not found"} + response_payload = self.spec.generate_from_ref( + "#/components/schemas/R2RResults_DocumentResponse_" + ) + response_payload["results"] = document + return 200, response_payload + + if method_upper == "DELETE" and len(segments) == 3: + doc_id = segments[2] + success = self.delete_document(doc_id) + response_payload = self.spec.generate_from_ref( + "#/components/schemas/R2RResults_GenericBooleanResponse_" + ) + results = response_payload.get("results") + if isinstance(results, Mapping): + results = copy.deepcopy(results) + results.update({"success": success}) + else: + results = {"success": success} + response_payload["results"] = results + status = 200 if success else 404 + return status, response_payload + + if method_upper == "PATCH" and len(segments) == 4 and segments[3] == "metadata": + doc_id = segments[2] + metadata_list = [] + if isinstance(json, list): + metadata_list = [dict(item) for item in json] + document = self.append_document_metadata(doc_id, metadata_list) + if document is None: + return 404, {"detail": f"Document {doc_id} not found"} + response_payload = self.spec.generate_from_ref( + "#/components/schemas/R2RResults_DocumentResponse_" + ) + response_payload["results"] = document + return 200, response_payload + + return super().handle(method=method, path=path, json=json, params=params, files=files) + + +class FirecrawlMockService(OpenAPIMockService): + """Stateful mock for Firecrawl map and scrape endpoints.""" + + def __init__(self, base_url: str, spec: OpenAPISpec) -> None: + super().__init__(base_url, spec) + self._maps: dict[str, list[str]] = {} + self._pages: dict[str, dict[str, Any]] = {} + + def register_map_result(self, origin: str, links: list[str]) -> None: + self._maps[origin] = list(links) + + def register_page(self, url: str, *, markdown: str | None = None, html: str | None = None, metadata: Mapping[str, Any] | None = None, links: list[str] | None = None) -> None: + self._pages[url] = { + "markdown": markdown, + "html": html, + "metadata": dict(metadata or {}), + "links": list(links or []), + } + + def _build_map_payload(self, target_url: str, limit: int | None) -> dict[str, Any]: + payload = self.spec.generate_from_ref("#/components/schemas/MapResponse") + links = self._maps.get(target_url, [target_url]) + if limit is not None: + try: + limit_value = int(limit) + if limit_value >= 0: + links = links[:limit_value] + except (TypeError, ValueError): + pass + payload["success"] = True + payload["links"] = [{"url": link} for link in links] + return payload + + def _default_metadata(self, url: str) -> dict[str, Any]: + metadata = self.spec.generate_from_ref("#/components/schemas/ScrapeMetadata") + metadata.update( + { + "url": url, + "sourceURL": url, + "scrapeId": metadata.get("scrapeId") or str(uuid4()), + "statusCode": metadata.get("statusCode", 200) or 200, + "contentType": metadata.get("contentType", "text/html") or "text/html", + "creditsUsed": metadata.get("creditsUsed", 1) or 1, + } + ) + return metadata + + def _build_scrape_payload(self, target_url: str, formats: list[str] | None) -> dict[str, Any]: + payload = self.spec.generate_from_ref("#/components/schemas/ScrapeResponse") + payload["success"] = True + page_info = self._pages.get(target_url, {}) + data = payload.get("data", {}) + markdown = page_info.get("markdown") or f"# Content for {target_url}\n" + html = page_info.get("html") or f"

Content for {target_url}

" + data.update( + { + "markdown": markdown, + "html": html, + "rawHtml": page_info.get("rawHtml", html), + "links": page_info.get("links", []), + } + ) + metadata_payload = self._default_metadata(target_url) + metadata_payload.update(page_info.get("metadata", {})) + data["metadata"] = metadata_payload + payload["data"] = data + return payload + + def map_response(self, url: str, limit: int | None = None) -> dict[str, Any]: + return self._build_map_payload(url, limit) + + def scrape_response(self, url: str, formats: list[str] | None = None) -> dict[str, Any]: + return self._build_scrape_payload(url, formats) + + def handle( + self, + *, + method: str, + path: str, + json: Mapping[str, Any] | None, + params: Mapping[str, Any] | None, + files: Any, + ) -> tuple[int, Any]: + normalized = self._normalize_path(path) + method_upper = method.upper() + + if normalized == "/v2/map" and method_upper == "POST": + body = json or {} + target_url = str(body.get("url", "")) + limit = body.get("limit") + return 200, self._build_map_payload(target_url, limit) + + if normalized == "/v2/scrape" and method_upper == "POST": + body = json or {} + target_url = str(body.get("url", "")) + formats = body.get("formats") + return 200, self._build_scrape_payload(target_url, formats) + + return super().handle(method=method, path=path, json=json, params=params, files=files) diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/__pycache__/__init__.cpython-312.pyc b/tests/unit/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f7bf11b Binary files /dev/null and b/tests/unit/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/cli/__init__.py b/tests/unit/cli/__init__.py new file mode 100644 index 0000000..f135283 --- /dev/null +++ b/tests/unit/cli/__init__.py @@ -0,0 +1 @@ +"""CLI tests package.""" diff --git a/tests/unit/cli/__pycache__/__init__.cpython-312.pyc b/tests/unit/cli/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..014e303 Binary files /dev/null and b/tests/unit/cli/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/cli/__pycache__/test_main_cli.cpython-312-pytest-8.4.2.pyc b/tests/unit/cli/__pycache__/test_main_cli.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..4fc9d8a Binary files /dev/null and b/tests/unit/cli/__pycache__/test_main_cli.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/cli/test_main_cli.py b/tests/unit/cli/test_main_cli.py new file mode 100644 index 0000000..8cf49d2 --- /dev/null +++ b/tests/unit/cli/test_main_cli.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +from types import SimpleNamespace +from uuid import uuid4 + +import pytest + +from ingest_pipeline.cli import main +from ingest_pipeline.core.models import ( + IngestionResult, + IngestionSource, + IngestionStatus, + StorageBackend, +) + + +@pytest.mark.parametrize( + ("collection_arg", "expected"), + ( + (None, "docs_example_com_web"), + ("explicit_collection", "explicit_collection"), + ), +) +@pytest.mark.asyncio +async def test_run_ingestion_collection_resolution(monkeypatch: pytest.MonkeyPatch, collection_arg: str | None, expected: str) -> None: + recorded: dict[str, object] = {} + + async def fake_flow(**kwargs: object) -> IngestionResult: + recorded.update(kwargs) + return IngestionResult( + job_id=uuid4(), + status=IngestionStatus.COMPLETED, + documents_processed=7, + documents_failed=0, + duration_seconds=1.5, + error_messages=[], + ) + + monkeypatch.setattr(main, "create_ingestion_flow", fake_flow) + + result = await main.run_ingestion( + url="https://docs.example.com/guide", + source_type=IngestionSource.WEB, + storage_backend=StorageBackend.WEAVIATE, + collection_name=collection_arg, + validate_first=False, + ) + + assert recorded["collection_name"] == expected + assert result.documents_processed == 7 + + +@pytest.mark.parametrize( + ("cron_value", "serve_now", "expected_type", "serve_expected"), + ( + ("0 * * * *", False, "cron", False), + (None, True, "interval", True), + ), +) +def test_schedule_creates_deployment( + monkeypatch: pytest.MonkeyPatch, + cron_value: str | None, + serve_now: bool, + expected_type: str, + serve_expected: bool, +) -> None: + recorded: dict[str, object] = {} + served = {"called": False} + + def fake_create_scheduled_deployment(**kwargs: object) -> str: + recorded.update(kwargs) + return "deployment" + + def fake_serve_deployments(deployments: list[str]) -> None: + served["called"] = True + assert deployments == ["deployment"] + + monkeypatch.setattr(main, "create_scheduled_deployment", fake_create_scheduled_deployment) + monkeypatch.setattr(main, "serve_deployments", fake_serve_deployments) + + main.schedule( + name="nightly", + source_url="https://example.com", + source_type=IngestionSource.WEB, + storage=StorageBackend.WEAVIATE, + cron=cron_value, + interval=30, + serve_now=serve_now, + ) + + assert recorded["schedule_type"] == expected_type + assert served["called"] is serve_expected + + +def test_serve_tui_launch(monkeypatch: pytest.MonkeyPatch) -> None: + invoked = {"count": 0} + + def fake_dashboard() -> None: + invoked["count"] = invoked["count"] + 1 + + monkeypatch.setattr("ingest_pipeline.cli.tui.dashboard", fake_dashboard) + + main.serve(ui="tui") + + assert invoked["count"] == 1 + + +@pytest.mark.asyncio +async def test_run_search_collects_results(monkeypatch: pytest.MonkeyPatch) -> None: + messages: list[object] = [] + + class DummyConsole: + def print(self, message: object) -> None: + messages.append(message) + + class WeaviateStub: + def __init__(self, config: object) -> None: + self.config = config + self.initialized = False + + async def initialize(self) -> None: + self.initialized = True + + async def search( + self, + query: str, + limit: int = 10, + threshold: float = 0.7, + *, + collection_name: str | None = None, + ) -> object: + yield SimpleNamespace(title="Title", content="Body text", score=0.91) + + dummy_settings = SimpleNamespace( + weaviate_endpoint="http://weaviate.local", + weaviate_api_key="token", + openwebui_endpoint="http://chat.local", + openwebui_api_key=None, + ) + + monkeypatch.setattr(main, "console", DummyConsole()) + monkeypatch.setattr(main, "get_settings", lambda: dummy_settings) + monkeypatch.setattr("ingest_pipeline.storage.weaviate.WeaviateStorage", WeaviateStub) + + await main.run_search("query", collection=None, backend="weaviate", limit=1) + + assert messages[-1] == "\n✅ [green]Found 1 results[/green]" diff --git a/tests/unit/flows/__init__.py b/tests/unit/flows/__init__.py new file mode 100644 index 0000000..8c6b782 --- /dev/null +++ b/tests/unit/flows/__init__.py @@ -0,0 +1 @@ +"""Flow tests package.""" diff --git a/tests/unit/flows/__pycache__/__init__.cpython-312.pyc b/tests/unit/flows/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cf9577d Binary files /dev/null and b/tests/unit/flows/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/flows/__pycache__/test_ingestion_flow.cpython-312-pytest-8.4.2.pyc b/tests/unit/flows/__pycache__/test_ingestion_flow.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..34f8b12 Binary files /dev/null and b/tests/unit/flows/__pycache__/test_ingestion_flow.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/flows/__pycache__/test_scheduler.cpython-312-pytest-8.4.2.pyc b/tests/unit/flows/__pycache__/test_scheduler.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..d386fd9 Binary files /dev/null and b/tests/unit/flows/__pycache__/test_scheduler.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/flows/test_ingestion_flow.py b/tests/unit/flows/test_ingestion_flow.py new file mode 100644 index 0000000..80c01bd --- /dev/null +++ b/tests/unit/flows/test_ingestion_flow.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +from types import SimpleNamespace +from unittest.mock import AsyncMock + +import pytest + +from ingest_pipeline.core.models import ( + IngestionJob, + IngestionSource, + StorageBackend, +) +from ingest_pipeline.flows import ingestion +from ingest_pipeline.flows.ingestion import ( + FirecrawlIngestor, + _chunk_urls, + _deduplicate_urls, + filter_existing_documents_task, + ingest_documents_task, +) + + +@pytest.mark.parametrize( + ("urls", "size", "expected"), + ( + (["a", "b", "c", "d"], 2, [["a", "b"], ["c", "d"]]), + (["solo"], 3, [["solo"]]), + ), +) +def test_chunk_urls_batches_urls(urls: list[str], size: int, expected: list[list[str]]) -> None: + assert _chunk_urls(urls, size) == expected + + +def test_chunk_urls_rejects_zero() -> None: + with pytest.raises(ValueError): + _chunk_urls(["item"], 0) + + +def test_deduplicate_urls_preserves_order() -> None: + urls = ["https://example.com", "https://example.com", "https://other.com"] + + unique = _deduplicate_urls(urls) + + assert unique == ["https://example.com", "https://other.com"] + + +@pytest.mark.asyncio +async def test_filter_existing_documents_task_filters_known_urls( + monkeypatch: pytest.MonkeyPatch, +) -> None: + existing_url = "https://keep.example.com" + new_url = "https://new.example.com" + existing_id = str(FirecrawlIngestor.compute_document_id(existing_url)) + + class StubStorage: + display_name = "stub-storage" + + async def check_exists( + self, + document_id: str, + *, + collection_name: str | None = None, + stale_after_days: int, + ) -> bool: + return document_id == existing_id + + monkeypatch.setattr( + ingestion, + "get_run_logger", + lambda: SimpleNamespace(info=lambda *args, **kwargs: None), + ) + + eligible = await filter_existing_documents_task.fn( + [existing_url, new_url], + StubStorage(), + stale_after_days=30, + ) + + assert eligible == [new_url] + + +@pytest.mark.asyncio +async def test_ingest_documents_task_invokes_helpers( + monkeypatch: pytest.MonkeyPatch, +) -> None: + job = IngestionJob( + source_url="https://docs.example.com", + source_type=IngestionSource.WEB, + storage_backend=StorageBackend.WEAVIATE, + ) + ingestor_sentinel = object() + storage_sentinel = object() + progress_events: list[tuple[int, str]] = [] + + def record_progress(percent: int, message: str) -> None: + progress_events.append((percent, message)) + + async def fake_create_storage( + job_arg: IngestionJob, + collection_name: str | None, + storage_block_name: str | None = None, + ) -> object: + return storage_sentinel + + async def fake_create_ingestor(job_arg: IngestionJob, config_block_name: str | None = None) -> object: + return ingestor_sentinel + + monkeypatch.setattr(ingestion, "_create_ingestor", fake_create_ingestor) + monkeypatch.setattr(ingestion, "_create_storage", fake_create_storage) + fake_process = AsyncMock(return_value=(5, 1)) + monkeypatch.setattr(ingestion, "_process_documents", fake_process) + + result = await ingest_documents_task.fn( + job, + collection_name="unit", + progress_callback=record_progress, + ) + + assert result == (5, 1) + assert progress_events == [ + (35, "Creating ingestor and storage clients..."), + (40, "Starting document processing..."), + ] + fake_process.assert_awaited_once_with( + ingestor_sentinel, + storage_sentinel, + job, + 50, + "unit", + record_progress, + ) diff --git a/tests/unit/flows/test_scheduler.py b/tests/unit/flows/test_scheduler.py new file mode 100644 index 0000000..d512ea1 --- /dev/null +++ b/tests/unit/flows/test_scheduler.py @@ -0,0 +1,72 @@ +from __future__ import annotations + +from datetime import timedelta +from types import SimpleNamespace + +import pytest + +from ingest_pipeline.flows import scheduler + + +def test_create_scheduled_deployment_cron(monkeypatch: pytest.MonkeyPatch) -> None: + captured: dict[str, object] = {} + + class DummyFlow: + def to_deployment(self, **kwargs: object) -> SimpleNamespace: + captured.update(kwargs) + return SimpleNamespace(**kwargs) + + monkeypatch.setattr(scheduler, "create_ingestion_flow", DummyFlow()) + + deployment = scheduler.create_scheduled_deployment( + name="cron-ingestion", + source_url="https://example.com", + source_type="web", + schedule_type="cron", + cron_expression="0 * * * *", + ) + + assert captured["schedule"].cron == "0 * * * *" + assert captured["parameters"]["source_type"] == "web" + assert deployment.tags == ["web", "weaviate"] + + +def test_create_scheduled_deployment_interval(monkeypatch: pytest.MonkeyPatch) -> None: + captured: dict[str, object] = {} + + class DummyFlow: + def to_deployment(self, **kwargs: object) -> SimpleNamespace: + captured.update(kwargs) + return SimpleNamespace(**kwargs) + + monkeypatch.setattr(scheduler, "create_ingestion_flow", DummyFlow()) + + deployment = scheduler.create_scheduled_deployment( + name="interval-ingestion", + source_url="https://repo.example.com", + source_type="repository", + storage_backend="open_webui", + schedule_type="interval", + interval_minutes=15, + tags=["custom"], + ) + + assert captured["schedule"].interval == timedelta(minutes=15) + assert captured["tags"] == ["custom"] + assert deployment.parameters["storage_backend"] == "open_webui" + + +def test_serve_deployments_invokes_prefect(monkeypatch: pytest.MonkeyPatch) -> None: + called: dict[str, object] = {} + + def fake_serve(*deployments: object, limit: int) -> None: + called["deployments"] = deployments + called["limit"] = limit + + monkeypatch.setattr(scheduler, "serve", fake_serve) + + deployment = SimpleNamespace(name="only") + scheduler.serve_deployments([deployment]) + + assert called["deployments"] == (deployment,) + assert called["limit"] == 10 diff --git a/tests/unit/ingestors/__init__.py b/tests/unit/ingestors/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/ingestors/__pycache__/__init__.cpython-312.pyc b/tests/unit/ingestors/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..cdae77c Binary files /dev/null and b/tests/unit/ingestors/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/ingestors/__pycache__/test_firecrawl_ingestor.cpython-312-pytest-8.4.2.pyc b/tests/unit/ingestors/__pycache__/test_firecrawl_ingestor.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..5e4e769 Binary files /dev/null and b/tests/unit/ingestors/__pycache__/test_firecrawl_ingestor.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/ingestors/__pycache__/test_repomix_ingestor.cpython-312-pytest-8.4.2.pyc b/tests/unit/ingestors/__pycache__/test_repomix_ingestor.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..10b949b Binary files /dev/null and b/tests/unit/ingestors/__pycache__/test_repomix_ingestor.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/ingestors/test_firecrawl_ingestor.py b/tests/unit/ingestors/test_firecrawl_ingestor.py new file mode 100644 index 0000000..d57d36d --- /dev/null +++ b/tests/unit/ingestors/test_firecrawl_ingestor.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +import pytest + +from ingest_pipeline.core.models import IngestionJob, IngestionSource, StorageBackend +from ingest_pipeline.ingestors.firecrawl import FirecrawlIngestor + + +@pytest.mark.asyncio +async def test_firecrawl_ingest_flows( + firecrawl_service, + firecrawl_client_stub, +) -> None: + base_url = "https://example.com" + page_urls = [f"{base_url}/docs", f"{base_url}/about"] + firecrawl_service.register_map_result(base_url, page_urls) + + for index, url in enumerate(page_urls, start=1): + firecrawl_service.register_page( + url, + markdown=f"# Page {index}\nContent body", + metadata={ + "title": f"Page {index}", + "description": f"Description {index}", + "statusCode": 200, + "language": "en", + "sourceURL": url, + }, + ) + + ingestor = FirecrawlIngestor() + job = IngestionJob( + source_type=IngestionSource.WEB, + source_url=base_url, + storage_backend=StorageBackend.WEAVIATE, + ) + + documents = [document async for document in ingestor.ingest(job)] + + assert {doc.metadata["title"] for doc in documents} == {"Page 1", "Page 2"} + assert all(doc.content.startswith("# Page") for doc in documents) + + +@pytest.mark.asyncio +async def test_firecrawl_validate_source( + firecrawl_service, + firecrawl_client_stub, +) -> None: + target_url = "https://validate.example.com" + firecrawl_service.register_page( + target_url, + markdown="# Validation Page\n", + metadata={ + "title": "Validation Page", + "statusCode": 200, + "language": "en", + "sourceURL": target_url, + }, + ) + + ingestor = FirecrawlIngestor() + + assert await ingestor.validate_source(target_url) is True + assert await ingestor.estimate_size(target_url) == 1 diff --git a/tests/unit/ingestors/test_repomix_ingestor.py b/tests/unit/ingestors/test_repomix_ingestor.py new file mode 100644 index 0000000..3090a6a --- /dev/null +++ b/tests/unit/ingestors/test_repomix_ingestor.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import pytest + +from ingest_pipeline.core.models import IngestionJob, IngestionSource, StorageBackend +from ingest_pipeline.ingestors.repomix import RepomixIngestor + + +@pytest.mark.parametrize( + ("content", "expected_keys"), + ( + ("## File: src/app.py\nprint(\"hi\")", ["src/app.py"]), + ("plain content without markers", ["repository"]), + ), +) +def test_split_by_files_detects_file_markers(content: str, expected_keys: list[str]) -> None: + ingestor = RepomixIngestor() + + results = ingestor._split_by_files(content) + + assert list(results) == expected_keys + + +@pytest.mark.parametrize( + ("content", "chunk_size", "expected"), + ( + ("line-one\nline-two\nline-three", 9, ["line-one", "line-two", "line-three"]), + ("single-line", 50, ["single-line"]), + ), +) +def test_chunk_content_respects_max_size( + content: str, + chunk_size: int, + expected: list[str], +) -> None: + ingestor = RepomixIngestor() + + chunks = ingestor._chunk_content(content, chunk_size=chunk_size) + + assert chunks == expected + + +@pytest.mark.parametrize( + ("file_path", "content", "expected"), + ( + ("src/app.py", "def feature():\n return True", "python"), + ("scripts/run", "#!/usr/bin/env python\nprint(\"ok\")", "python"), + ("documentation.md", "# Title", "markdown"), + ("unknown.ext", "text", None), + ), +) +def test_detect_programming_language_infers_extension( + file_path: str, + content: str, + expected: str | None, +) -> None: + ingestor = RepomixIngestor() + + detected = ingestor._detect_programming_language(file_path, content) + + assert detected == expected + + +def test_create_document_enriches_metadata() -> None: + ingestor = RepomixIngestor() + job = IngestionJob( + source_url="https://example.com/repo.git", + source_type=IngestionSource.REPOSITORY, + storage_backend=StorageBackend.WEAVIATE, + ) + + document = ingestor._create_document( + "src/module.py", + "def alpha():\n return 42\n", + job, + chunk_index=1, + git_metadata={"branch_name": "main", "commit_hash": "deadbeef"}, + repo_info={"repository_name": "demo"}, + ) + + assert document.metadata["repository_name"] == "demo" + assert document.metadata["branch_name"] == "main" + assert document.metadata["commit_hash"] == "deadbeef" + assert document.metadata["title"].endswith("(chunk 1)") + assert document.collection == job.storage_backend.value diff --git a/tests/unit/storage/__init__.py b/tests/unit/storage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/storage/__pycache__/__init__.cpython-312.pyc b/tests/unit/storage/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..80dba8b Binary files /dev/null and b/tests/unit/storage/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/storage/__pycache__/test_base_storage.cpython-312-pytest-8.4.2.pyc b/tests/unit/storage/__pycache__/test_base_storage.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..f7e88c5 Binary files /dev/null and b/tests/unit/storage/__pycache__/test_base_storage.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/storage/__pycache__/test_openwebui.cpython-312-pytest-8.4.2.pyc b/tests/unit/storage/__pycache__/test_openwebui.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..52b6cd5 Binary files /dev/null and b/tests/unit/storage/__pycache__/test_openwebui.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/storage/__pycache__/test_r2r_helpers.cpython-312-pytest-8.4.2.pyc b/tests/unit/storage/__pycache__/test_r2r_helpers.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..46de2da Binary files /dev/null and b/tests/unit/storage/__pycache__/test_r2r_helpers.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/storage/__pycache__/test_weaviate_helpers.cpython-312-pytest-8.4.2.pyc b/tests/unit/storage/__pycache__/test_weaviate_helpers.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..8c7cdf2 Binary files /dev/null and b/tests/unit/storage/__pycache__/test_weaviate_helpers.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/storage/test_base_storage.py b/tests/unit/storage/test_base_storage.py new file mode 100644 index 0000000..6e9a860 --- /dev/null +++ b/tests/unit/storage/test_base_storage.py @@ -0,0 +1,102 @@ +from __future__ import annotations + +from datetime import UTC, datetime, timedelta + +import pytest + +from ingest_pipeline.core.models import Document, StorageConfig +from ingest_pipeline.storage.base import BaseStorage + + +class StubStorage(BaseStorage): + """Concrete BaseStorage for exercising shared logic.""" + + def __init__( + self, + config: StorageConfig, + *, + result: Document | None = None, + error: BaseException | None = None, + ) -> None: + super().__init__(config) + self._result = result + self._error = error + + async def initialize(self) -> None: # pragma: no cover - not used in tests + return None + + async def store( + self, + document: Document, + *, + collection_name: str | None = None, + ) -> str: # pragma: no cover + return "" + + async def store_batch( + self, + documents: list[Document], + *, + collection_name: str | None = None, + ) -> list[str]: # pragma: no cover + return [] + + async def delete( + self, + document_id: str, + *, + collection_name: str | None = None, + ) -> bool: # pragma: no cover + return False + + async def retrieve( + self, + document_id: str, + *, + collection_name: str | None = None, + ) -> Document | None: + if self._error is not None: + raise self._error + return self._result + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "delta_days", + [pytest.param(0, id="fresh"), pytest.param(40, id="stale")], +) +async def test_check_exists_uses_staleness(document_factory, storage_config, delta_days) -> None: + """Return True only when document timestamp is within freshness window.""" + + timestamp = datetime.now(UTC) - timedelta(days=delta_days) + document = document_factory(content=f"doc-{delta_days}", metadata_updates={"timestamp": timestamp}) + storage = StubStorage(storage_config, result=document) + + outcome = await storage.check_exists("identifier", stale_after_days=30) + + assert outcome is (delta_days <= 30) + + +@pytest.mark.asyncio +async def test_check_exists_returns_false_for_missing(storage_config) -> None: + """Return False when retrieve yields no document.""" + + storage = StubStorage(storage_config, result=None) + + assert await storage.check_exists("missing") is False + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "error_factory", + [ + pytest.param(lambda: NotImplementedError("unsupported"), id="not-implemented"), + pytest.param(lambda: RuntimeError("unexpected"), id="generic-error"), + ], +) +async def test_check_exists_swallows_errors(storage_config, error_factory) -> None: + """Return False when retrieval raises errors.""" + + storage = StubStorage(storage_config, error=error_factory()) + + assert await storage.check_exists("identifier") is False diff --git a/tests/unit/storage/test_openwebui.py b/tests/unit/storage/test_openwebui.py new file mode 100644 index 0000000..cb84050 --- /dev/null +++ b/tests/unit/storage/test_openwebui.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from ingest_pipeline.core.models import StorageConfig +from ingest_pipeline.storage.openwebui import OpenWebUIStorage + + +def _make_storage(config: StorageConfig) -> OpenWebUIStorage: + return OpenWebUIStorage(config) + + +@pytest.mark.asyncio +async def test_get_knowledge_id_returns_existing( + storage_config: StorageConfig, + openwebui_service, + httpx_stub, +) -> None: + """Return cached identifier when knowledge base already exists.""" + + openwebui_service.ensure_knowledge( + name=storage_config.collection_name, + knowledge_id="kb-123", + ) + storage = _make_storage(storage_config) + + knowledge_id = await storage._get_knowledge_id(None, create=False) + + assert knowledge_id == "kb-123" + urls = [request["url"] for request in httpx_stub.requests] + assert "http://storage.local/api/v1/knowledge/list" in urls + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_get_knowledge_id_creates_when_missing( + storage_config: StorageConfig, + openwebui_service, + httpx_stub, +) -> None: + """Create knowledge base when missing and caching the result.""" + + derived_config = storage_config.model_copy(update={"collection_name": "custom"}) + storage = _make_storage(derived_config) + + knowledge_id = await storage._get_knowledge_id("custom", create=True) + + assert knowledge_id is not None + urls = [request["url"] for request in httpx_stub.requests] + assert "http://storage.local/api/v1/knowledge/list" in urls + assert any( + url.startswith("http://storage.local/api/v1/knowledge/") and url.endswith("/create") + for url in urls + ) + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_store_uploads_and_attaches_document( + document_factory, + storage_config: StorageConfig, + openwebui_service, + httpx_stub, +) -> None: + """Upload file then attach it to the knowledge base.""" + + storage = _make_storage(storage_config) + document = document_factory(content="hello world", metadata_updates={"title": "example"}) + + file_id = await storage.store(document) + + assert file_id is not None + urls = [request["url"] for request in httpx_stub.requests] + assert any(url.endswith("/api/v1/files/") for url in urls) + assert any("/file/add" in url for url in urls) + knowledge_entry = openwebui_service.find_knowledge_by_name(storage_config.collection_name) + assert knowledge_entry is not None + _, knowledge = knowledge_entry + assert len(knowledge.get("files", [])) == 1 + assert knowledge["files"][0]["id"] == file_id + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_store_batch_handles_multiple_documents( + document_factory, + storage_config: StorageConfig, + openwebui_service, + httpx_stub, +) -> None: + """Store documents in batch and return collected file identifiers.""" + + storage = _make_storage(storage_config) + first = document_factory(content="alpha", metadata_updates={"title": "alpha"}) + second = document_factory(content="beta", metadata_updates={"title": "beta"}) + + file_ids = await storage.store_batch([first, second]) + + assert len(file_ids) == 2 + files_payloads: list[Any] = [request["files"] for request in httpx_stub.requests if request["method"] == "POST"] + assert any(payload is not None for payload in files_payloads) + knowledge_entry = openwebui_service.find_knowledge_by_name(storage_config.collection_name) + assert knowledge_entry is not None + _, knowledge = knowledge_entry + assert {meta["id"] for meta in knowledge.get("files", [])} == set(file_ids) + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_delete_removes_file( + storage_config: StorageConfig, + openwebui_service, + httpx_stub, +) -> None: + """Remove file from knowledge base and delete the uploaded resource.""" + + openwebui_service.ensure_knowledge( + name=storage_config.collection_name, + knowledge_id="kb-55", + ) + openwebui_service.create_file(filename="to-delete.txt", file_id="file-xyz") + openwebui_service.attach_existing_file("kb-55", "file-xyz") + storage = _make_storage(storage_config) + + result = await storage.delete("file-xyz") + + assert result is True + urls = [request["url"] for request in httpx_stub.requests] + assert "http://storage.local/api/v1/knowledge/kb-55/file/remove" in urls + assert "http://storage.local/api/v1/files/file-xyz" in urls + knowledge = openwebui_service.get_knowledge("kb-55") + assert knowledge is not None + assert knowledge.get("files", []) == [] + await storage.client.aclose() diff --git a/tests/unit/storage/test_r2r_helpers.py b/tests/unit/storage/test_r2r_helpers.py new file mode 100644 index 0000000..fdbc400 --- /dev/null +++ b/tests/unit/storage/test_r2r_helpers.py @@ -0,0 +1,343 @@ +from __future__ import annotations + +from collections.abc import Mapping +from datetime import UTC, datetime +from types import SimpleNamespace +from typing import Any, Self + +import pytest + +from ingest_pipeline.core.models import StorageConfig +from ingest_pipeline.storage.r2r.storage import ( + R2RStorage, + _as_datetime, + _as_int, + _as_mapping, + _as_sequence, + _extract_id, +) + + +@pytest.fixture +def r2r_client_stub( + monkeypatch: pytest.MonkeyPatch, + r2r_service, +) -> object: + class DummyR2RException(Exception): + def __init__(self, message: str, status_code: int | None = None) -> None: + super().__init__(message) + self.status_code = status_code + + class MockResponse: + def __init__(self, json_data: dict[str, Any], status_code: int = 200) -> None: + self._json_data = json_data + self.status_code = status_code + + def json(self) -> dict[str, Any]: + return self._json_data + + def raise_for_status(self) -> None: + if self.status_code >= 400: + from httpx import HTTPStatusError + raise HTTPStatusError("HTTP error", request=None, response=self) + + class MockAsyncClient: + def __init__(self, service: Any) -> None: + self._service = service + + async def get(self, url: str) -> MockResponse: + if "/v3/collections" in url: + # Return existing collections + collections = [] + for collection_id, collection_data in self._service._collections.items(): + collections.append({ + "id": collection_id, + "name": collection_data["name"], + "description": collection_data.get("description", ""), + }) + return MockResponse({"results": collections}) + return MockResponse({}) + + async def post(self, url: str, *, json: dict[str, Any] | None = None, files: dict[str, Any] | None = None) -> MockResponse: + if "/v3/collections" in url and json: + # Create new collection + new_collection_id = f"col-{len(self._service._collections) + 1}" + self._service.create_collection( + name=json["name"], + collection_id=new_collection_id, + description=json.get("description", ""), + ) + return MockResponse({ + "results": { + "id": new_collection_id, + "name": json["name"], + "description": json.get("description", ""), + } + }) + elif "/v3/documents" in url and files: + # Create document + import json as json_lib + document_id = files.get("id", (None, f"doc-{len(self._service._documents) + 1}"))[1] + content = files.get("raw_text", (None, ""))[1] + metadata_str = files.get("metadata", (None, "{}"))[1] + metadata = json_lib.loads(metadata_str) if metadata_str else {} + + # Store document in mock service + document_data = { + "id": document_id, + "content": content, + "metadata": metadata, + } + self._service._documents[document_id] = document_data + + # Update collection document count if specified + collection_ids = files.get("collection_ids") + if collection_ids: + # collection_ids is passed as a tuple (None, "[collection_id]") in the files format + collection_ids_str = collection_ids[1] if isinstance(collection_ids, tuple) else collection_ids + try: + import json as json_lib + collection_list = json_lib.loads(collection_ids_str) if isinstance(collection_ids_str, str) else collection_ids_str + if isinstance(collection_list, list) and len(collection_list) > 0: + # Extract the collection ID - it could be a string or dict + first_collection = collection_list[0] + if isinstance(first_collection, dict) and "id" in first_collection: + collection_id = first_collection["id"] + elif isinstance(first_collection, str): + collection_id = first_collection + else: + collection_id = None + + if collection_id and collection_id in self._service._collections: + # Update the collection's document count to match the actual number of documents + total_docs = len(self._service._documents) + self._service._collections[collection_id]["document_count"] = total_docs + except (json_lib.JSONDecodeError, TypeError, KeyError): + pass # Ignore parsing errors + + return MockResponse({ + "results": { + "document_id": document_id, + "message": "Document created successfully", + } + }) + return MockResponse({}) + + async def aclose(self) -> None: + return None + + async def __aenter__(self) -> Self: + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + return None + + class DocumentsAPI: + def __init__(self, service: Any) -> None: + self._service = service + + async def retrieve(self, document_id: str) -> dict[str, Any]: + document = self._service.get_document(document_id) + if document is None: + raise DummyR2RException("Not found", status_code=404) + return {"results": document} + + async def delete(self, document_id: str) -> dict[str, Any]: + if not self._service.delete_document(document_id): + raise DummyR2RException("Not found", status_code=404) + return {"results": {"success": True}} + + async def append_metadata(self, id: str, metadata: list[dict[str, Any]]) -> dict[str, Any]: + document = self._service.append_document_metadata(id, metadata) + if document is None: + raise DummyR2RException("Not found", status_code=404) + return {"results": document} + + class RetrievalAPI: + def __init__(self, service: Any) -> None: + self._service = service + + async def search(self, query: str, search_settings: Mapping[str, Any]) -> dict[str, Any]: + results = [ + {"document_id": doc_id, "score": 1.0} + for doc_id in self._service._documents + ] + return {"results": results} + + class DummyClient: + def __init__(self, service: Any) -> None: + self.documents = DocumentsAPI(service) + self.retrieval = RetrievalAPI(service) + + async def aclose(self) -> None: + return None + + async def close(self) -> None: + return None + + # Mock the AsyncClient that R2RStorage uses internally + mock_async_client = MockAsyncClient(r2r_service) + monkeypatch.setattr( + "ingest_pipeline.storage.r2r.storage.AsyncClient", + lambda: mock_async_client, + ) + + client = DummyClient(r2r_service) + monkeypatch.setattr( + "ingest_pipeline.storage.r2r.storage.R2RAsyncClient", + lambda endpoint: client, + ) + monkeypatch.setattr( + "ingest_pipeline.storage.r2r.storage.R2RException", + DummyR2RException, + ) + return client + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + pytest.param({"a": 1}, {"a": 1}, id="mapping"), + pytest.param(SimpleNamespace(a=2), {"a": 2}, id="namespace"), + pytest.param(5, {}, id="other"), + ], +) +def test_as_mapping_normalizes(value, expected) -> None: + """Convert inputs into dictionaries where possible.""" + + assert _as_mapping(value) == expected + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + pytest.param([1, 2], (1, 2), id="list"), + pytest.param((3, 4), (3, 4), id="tuple"), + pytest.param("ab", ("a", "b"), id="string"), + pytest.param(7, (), id="non-iterable"), + ], +) +def test_as_sequence_coerces_iterables(value, expected) -> None: + """Represent values as tuples for downstream iteration.""" + + assert _as_sequence(value) == expected + + +@pytest.mark.parametrize( + ("source", "fallback", "expected"), + [ + pytest.param({"id": "abc"}, "x", "abc", id="mapping"), + pytest.param(SimpleNamespace(id=123), "x", "123", id="attribute"), + pytest.param({}, "fallback", "fallback", id="fallback"), + ], +) +def test_extract_id_falls_back(source, fallback, expected) -> None: + """Prefer embedded identifier values and fall back otherwise.""" + + assert _extract_id(source, fallback) == expected + + +@pytest.mark.parametrize( + ("value", "expected_year"), + [ + pytest.param(datetime(2024, 1, 1, tzinfo=UTC), 2024, id="datetime"), + pytest.param("2024-02-01T00:00:00+00:00", 2024, id="iso"), + pytest.param("invalid", datetime.now(UTC).year, id="fallback"), + ], +) +def test_as_datetime_recognizes_formats(value, expected_year) -> None: + """Produce timezone-aware datetime objects.""" + + assert _as_datetime(value).year == expected_year + + +@pytest.mark.parametrize( + ("value", "expected"), + [ + pytest.param(True, 1, id="bool"), + pytest.param(5, 5, id="int"), + pytest.param(3.9, 3, id="float"), + pytest.param("7", 7, id="string"), + pytest.param("8.2", 8, id="float-string"), + pytest.param("bad", 2, id="default"), + ], +) +def test_as_int_handles_numeric_coercions(value, expected) -> None: + """Convert assorted numeric representations.""" + + assert _as_int(value, default=2) == expected + + +@pytest.mark.asyncio +async def test_ensure_collection_finds_existing( + r2r_storage_config: StorageConfig, + r2r_service, + httpx_stub, + r2r_client_stub, +) -> None: + """Return collection identifier when already present.""" + + r2r_service.create_collection( + name=r2r_storage_config.collection_name, + collection_id="col-1", + ) + storage = R2RStorage(r2r_storage_config) + + collection_id = await storage._ensure_collection(r2r_storage_config.collection_name) + + assert collection_id == "col-1" + assert storage.default_collection_id == "col-1" + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_ensure_collection_creates_when_missing( + r2r_storage_config: StorageConfig, + r2r_service, + httpx_stub, + r2r_client_stub, +) -> None: + """Create collection via POST when absent.""" + + storage = R2RStorage(r2r_storage_config) + + collection_id = await storage._ensure_collection("alternate") + + assert collection_id is not None + located = r2r_service.find_collection_by_name("alternate") + assert located is not None + identifier, _ = located + assert identifier == collection_id + await storage.client.aclose() + + +@pytest.mark.asyncio +async def test_store_batch_creates_documents( + document_factory, + r2r_storage_config: StorageConfig, + r2r_service, + httpx_stub, + r2r_client_stub, +) -> None: + """Store documents and persist them via the R2R mock service.""" + + storage = R2RStorage(r2r_storage_config) + documents = [ + document_factory(content="first document", metadata_updates={"title": "First"}), + document_factory(content="second document", metadata_updates={"title": "Second"}), + ] + + stored_ids = await storage.store_batch(documents) + + assert len(stored_ids) == 2 + for doc_id, original in zip(stored_ids, documents, strict=False): + stored = r2r_service.get_document(doc_id) + assert stored is not None + assert stored["metadata"]["source_url"] == original.metadata["source_url"] + + collection = r2r_service.find_collection_by_name(r2r_storage_config.collection_name) + assert collection is not None + _, collection_payload = collection + assert collection_payload["document_count"] == 2 + + await storage.client.aclose() diff --git a/tests/unit/storage/test_weaviate_helpers.py b/tests/unit/storage/test_weaviate_helpers.py new file mode 100644 index 0000000..1992ba5 --- /dev/null +++ b/tests/unit/storage/test_weaviate_helpers.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +from datetime import UTC, datetime +from typing import cast + +import pytest + +from ingest_pipeline.core.exceptions import StorageError +from ingest_pipeline.core.models import IngestionSource, StorageConfig +from ingest_pipeline.storage.weaviate import WeaviateStorage +from ingest_pipeline.utils.vectorizer import Vectorizer + + +def _build_storage(config: StorageConfig) -> WeaviateStorage: + storage = WeaviateStorage.__new__(WeaviateStorage) + storage.config = config + storage.client = None + storage.vectorizer = cast(Vectorizer, None) + storage._default_collection = config.collection_name + return storage + + +@pytest.mark.parametrize( + "raw, expected", + [ + pytest.param([1, 2, 3], [1.0, 2.0, 3.0], id="list"), + pytest.param({"default": [4, 5]}, [4.0, 5.0], id="mapping"), + pytest.param([[6, 7]], [6.0, 7.0], id="nested"), + pytest.param("invalid", None, id="invalid"), + ], +) +def test_extract_vector_normalizes(raw, expected) -> None: + """Normalize various vector payload structures.""" + + assert WeaviateStorage._extract_vector(raw) == expected + + +@pytest.mark.parametrize( + "value, expected", + [ + pytest.param(IngestionSource.WEB, IngestionSource.WEB, id="enum"), + pytest.param("documentation", IngestionSource.DOCUMENTATION, id="string"), + pytest.param("unknown", IngestionSource.WEB, id="fallback"), + pytest.param(42, IngestionSource.WEB, id="non-string"), + ], +) +def test_parse_source_normalizes(value, expected) -> None: + """Ensure ingestion source strings coerce into enums.""" + + assert WeaviateStorage._parse_source(value) is expected + + +def test_coerce_properties_returns_mapping() -> None: + """Pass through mapping when provided.""" + + payload = {"key": "value"} + + assert WeaviateStorage._coerce_properties(payload, context="test") is payload + + +def test_coerce_properties_allows_missing() -> None: + """Return None when allow_missing is True and payload absent.""" + + assert WeaviateStorage._coerce_properties(None, context="test", allow_missing=True) is None + + +@pytest.mark.parametrize( + "payload", + [pytest.param(None, id="none"), pytest.param([1, 2, 3], id="sequence")], +) +def test_coerce_properties_raises_on_invalid(payload) -> None: + """Raise storage error when payload is not a mapping.""" + + with pytest.raises(StorageError): + WeaviateStorage._coerce_properties(payload, context="invalid") + + +@pytest.mark.parametrize( + "input_name, expected", + [ + pytest.param(None, "Documents", id="default"), + pytest.param(" custom ", "Custom", id="trimmed"), + ], +) +def test_normalize_collection_name(storage_config, input_name, expected) -> None: + """Normalize with capitalization and fallback to config value.""" + + storage = _build_storage(storage_config) + + assert storage._normalize_collection_name(input_name) == expected + + +def test_normalize_collection_name_rejects_empty(storage_config) -> None: + """Raise when provided name is blank.""" + + storage = _build_storage(storage_config) + + with pytest.raises(StorageError): + storage._normalize_collection_name(" ") + + +@pytest.mark.parametrize( + "value, expected", + [ + pytest.param(5, 5, id="int"), + pytest.param(7.9, 7, id="float"), + pytest.param("12", 12, id="string"), + pytest.param(None, 0, id="none"), + ], +) +def test_safe_convert_count(storage_config, value, expected) -> None: + """Convert assorted count representations into integers.""" + + storage = _build_storage(storage_config) + + assert storage._safe_convert_count(value) == expected + + +def test_build_document_metadata(storage_config) -> None: + """Build metadata mapping with type coercions.""" + + storage = _build_storage(storage_config) + props = { + "source_url": "https://example.com", + "title": "Doc", + "description": "Details", + "timestamp": "2024-01-01T00:00:00+00:00", + "content_type": "text/plain", + "word_count": "5", + "char_count": 128.0, + } + + metadata = storage._build_document_metadata(props) + + assert metadata["source_url"] == "https://example.com" + assert metadata["title"] == "Doc" + assert metadata["description"] == "Details" + assert metadata["timestamp"] == datetime(2024, 1, 1, tzinfo=UTC) + assert metadata["word_count"] == 5 + assert metadata["char_count"] == 128 diff --git a/tests/unit/tui/__init__.py b/tests/unit/tui/__init__.py new file mode 100644 index 0000000..10a8ae2 --- /dev/null +++ b/tests/unit/tui/__init__.py @@ -0,0 +1 @@ +"""TUI tests package.""" diff --git a/tests/unit/tui/__pycache__/__init__.cpython-312.pyc b/tests/unit/tui/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..0316e93 Binary files /dev/null and b/tests/unit/tui/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/tui/__pycache__/test_dashboard_screen.cpython-312-pytest-8.4.2.pyc b/tests/unit/tui/__pycache__/test_dashboard_screen.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..851398c Binary files /dev/null and b/tests/unit/tui/__pycache__/test_dashboard_screen.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/tui/__pycache__/test_storage_manager.cpython-312-pytest-8.4.2.pyc b/tests/unit/tui/__pycache__/test_storage_manager.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..4ce1491 Binary files /dev/null and b/tests/unit/tui/__pycache__/test_storage_manager.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/tui/test_dashboard_screen.py b/tests/unit/tui/test_dashboard_screen.py new file mode 100644 index 0000000..7b89b0f --- /dev/null +++ b/tests/unit/tui/test_dashboard_screen.py @@ -0,0 +1,215 @@ +from __future__ import annotations + +import pytest +from textual.app import App + +from ingest_pipeline.cli.tui.models import CollectionInfo +from ingest_pipeline.cli.tui.screens.dashboard import CollectionOverviewScreen +from ingest_pipeline.cli.tui.screens.documents import DocumentManagementScreen +from ingest_pipeline.cli.tui.widgets.tables import EnhancedDataTable +from ingest_pipeline.core.models import StorageBackend, StorageConfig +from ingest_pipeline.storage.base import BaseStorage + + +class StorageManagerStub: + def __init__(self, collections: list[CollectionInfo], backends: dict[StorageBackend, BaseStorage]) -> None: + self._collections = collections + self._available = list(backends.keys()) + self.backends = backends + self.is_initialized = True + + async def initialize_all_backends(self) -> dict[StorageBackend, bool]: + self.is_initialized = True + return dict.fromkeys(self._available, True) + + async def get_all_collections(self) -> list[CollectionInfo]: + return self._collections + + def get_available_backends(self) -> list[StorageBackend]: + return self._available + + def get_backend(self, backend: StorageBackend) -> BaseStorage | None: + return self.backends.get(backend) + + async def close_all(self) -> None: + return None + + +class PassiveStorage(BaseStorage): + def __init__(self, backend: StorageBackend) -> None: + super().__init__( + StorageConfig( + backend=backend, + endpoint="http://example.com", + collection_name="collection", + ) + ) + + async def initialize(self) -> None: + return None + + async def store(self, document, *, collection_name: str | None = None) -> str: # pragma: no cover - unused + raise NotImplementedError + + async def store_batch(self, documents, *, collection_name: str | None = None): # pragma: no cover - unused + raise NotImplementedError + + async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: # pragma: no cover - unused + raise NotImplementedError + + +class ScreenTestApp(App[None]): + def __init__(self, screen: CollectionOverviewScreen) -> None: + super().__init__() + self._screen = screen + + async def on_mount(self) -> None: + await self.push_screen(self._screen) + + +@pytest.mark.asyncio +async def test_collection_overview_table_reflects_collections(monkeypatch: pytest.MonkeyPatch) -> None: + collections: list[CollectionInfo] = [ + { + "name": "docs_example_com", + "type": "weaviate", + "count": 1250, + "backend": "🗄️ Weaviate", + "status": "✓ Active", + "last_updated": "2024-01-01T00:00:00Z", + "size_mb": 1800.0, + }, + { + "name": "repo_tools", + "type": "openwebui", + "count": 42, + "backend": "🌐 OpenWebUI", + "status": "✓ Active", + "last_updated": "2024-01-02T12:00:00Z", + "size_mb": 12.5, + }, + ] + + storage_manager = StorageManagerStub( + collections, + { + StorageBackend.WEAVIATE: PassiveStorage(StorageBackend.WEAVIATE), + StorageBackend.OPEN_WEBUI: PassiveStorage(StorageBackend.OPEN_WEBUI), + }, + ) + + screen = CollectionOverviewScreen( + storage_manager, + weaviate=storage_manager.get_backend(StorageBackend.WEAVIATE), + openwebui=storage_manager.get_backend(StorageBackend.OPEN_WEBUI), + r2r=None, + ) + + app = ScreenTestApp(screen) + + async with app.run_test() as _: + worker = screen.refresh_collections() + await worker.wait() + table = screen.query_one("#collections_table", EnhancedDataTable) + first_row = table.get_row_at(0) + second_row = table.get_row_at(1) + + assert first_row == [ + "docs_example_com", + "🗄️ Weaviate", + "1,250", + "1.8 GB", + "📖 Docs", + "✓ Active", + "2024-01-01T00:00:00Z", + ] + assert second_row == [ + "repo_tools", + "🌐 OpenWebUI", + "42", + "12.5 MB", + "📦 Code", + "✓ Active", + "2024-01-02T12:00:00Z", + ] + + assert screen.total_collections == 2 + assert screen.total_documents == 1292 + assert screen.active_backends == 2 + +class DocumentStorageStub(BaseStorage): + def __init__(self) -> None: + super().__init__( + StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint="http://example.com", + collection_name="docs", + ) + ) + + async def initialize(self) -> None: + return None + + async def store(self, document, *, collection_name: str | None = None) -> str: # pragma: no cover - unused + raise NotImplementedError + + async def store_batch(self, documents, *, collection_name: str | None = None): # pragma: no cover - unused + raise NotImplementedError + + async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: # pragma: no cover - unused + raise NotImplementedError + + async def list_documents(self, *, limit: int = 100, offset: int = 0, collection_name: str | None = None): + return [ + { + "id": "doc-1234567890", + "title": "Deep Dive into Markdown Rendering", + "source_url": "https://example.com/post", + "description": "Extensive article on rendering Markdown content in complex UIs", + "content_type": "text/markdown", + "content_preview": "# Heading\nContent", + "word_count": 321, + "timestamp": "2024-03-04T10:15:00+00:00", + } + ] + + +def build_collection(count: int) -> CollectionInfo: + return { + "name": "docs_example_com", + "type": "weaviate", + "count": count, + "backend": "🗄️ Weaviate", + "status": "✓ Active", + "last_updated": "2024-03-04T10:15:00Z", + "size_mb": 42.0, + } + + +class DocumentScreenTestApp(App[None]): + def __init__(self, screen) -> None: + super().__init__() + self._screen = screen + + async def on_mount(self) -> None: + await self.push_screen(self._screen) + + +@pytest.mark.asyncio +async def test_document_management_screen_formats_rows() -> None: + storage = DocumentStorageStub() + screen = DocumentManagementScreen(build_collection(1), storage) + app = DocumentScreenTestApp(screen) + + async with app.run_test() as pilot: + await pilot.pause() + table = screen.query_one("#documents_table", EnhancedDataTable) + row = table.get_row_at(0) + + assert row[1] == "Deep Dive into Markdown Rendering" + assert row[2] == "https://example.com/post" + assert row[3].startswith("Extensive article") + assert row[4] == "📝 md" + assert row[5] == "321" + assert row[6] == "03/04 10:15" + assert row[7].startswith("doc-1234") diff --git a/tests/unit/tui/test_storage_manager.py b/tests/unit/tui/test_storage_manager.py new file mode 100644 index 0000000..f95833d --- /dev/null +++ b/tests/unit/tui/test_storage_manager.py @@ -0,0 +1,172 @@ +from __future__ import annotations + +from types import SimpleNamespace + +import pytest + +from ingest_pipeline.cli.tui.utils.storage_manager import MultiStorageAdapter, StorageManager +from ingest_pipeline.core.exceptions import StorageError +from ingest_pipeline.core.models import Document, StorageBackend, StorageConfig +from ingest_pipeline.storage.base import BaseStorage + + +class StubStorage(BaseStorage): + def __init__(self, config: StorageConfig, *, documents: list[Document] | None = None, fail: bool = False) -> None: + super().__init__(config) + self.documents = documents or [] + self.fail = fail + self.stored: list[Document] = [] + + async def initialize(self) -> None: + return None + + async def store(self, document: Document, *, collection_name: str | None = None) -> str: + self.stored.append(document) + if self.fail: + raise RuntimeError("store failed") + return f"{self.config.backend.value}-single" + + async def store_batch(self, documents: list[Document], *, collection_name: str | None = None) -> list[str]: + self.stored.extend(documents) + if self.fail: + raise RuntimeError("batch failed") + return [f"{self.config.backend.value}-{index}" for index in range(len(documents))] + + async def delete(self, document_id: str, *, collection_name: str | None = None) -> bool: + if self.fail: + raise RuntimeError("delete failed") + return True + + async def count(self, *, collection_name: str | None = None) -> int: + return len(self.documents) + + async def list_collections(self) -> list[str]: + return ["collection"] + + async def search( + self, + query: str, + limit: int = 10, + threshold: float = 0.7, + *, + collection_name: str | None = None, + ): + for document in self.documents: + yield document + + async def close(self) -> None: + return None + + +@pytest.mark.asyncio +async def test_multi_storage_adapter_reports_replication_failure(document_factory) -> None: + primary_config = StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint="http://weaviate.local", + collection_name="primary", + ) + secondary_config = StorageConfig( + backend=StorageBackend.OPEN_WEBUI, + endpoint="http://chat.local", + collection_name="secondary", + ) + + primary = StubStorage(primary_config) + secondary = StubStorage(secondary_config, fail=True) + adapter = MultiStorageAdapter([primary, secondary]) + + with pytest.raises(StorageError): + await adapter.store(document_factory(content="payload")) + + assert primary.stored[0].content == "payload" + + +def test_storage_manager_build_multi_storage_adapter_deduplicates(document_factory) -> None: + settings = SimpleNamespace( + weaviate_endpoint="http://weaviate.local", + weaviate_api_key=None, + openwebui_endpoint="http://chat.local", + openwebui_api_key=None, + r2r_endpoint=None, + r2r_api_key=None, + ) + manager = StorageManager(settings) + + weaviate_config = StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint="http://weaviate.local", + collection_name="primary", + ) + openwebui_config = StorageConfig( + backend=StorageBackend.OPEN_WEBUI, + endpoint="http://chat.local", + collection_name="secondary", + ) + + manager.backends[StorageBackend.WEAVIATE] = StubStorage(weaviate_config) + manager.backends[StorageBackend.OPEN_WEBUI] = StubStorage(openwebui_config) + + adapter = manager.build_multi_storage_adapter( + [StorageBackend.WEAVIATE, StorageBackend.WEAVIATE, StorageBackend.OPEN_WEBUI] + ) + + assert len(adapter._storages) == 2 + assert adapter._storages[0].config.backend == StorageBackend.WEAVIATE + assert adapter._storages[1].config.backend == StorageBackend.OPEN_WEBUI + + +def test_storage_manager_build_multi_storage_adapter_missing_backend() -> None: + settings = SimpleNamespace( + weaviate_endpoint="http://weaviate.local", + weaviate_api_key=None, + openwebui_endpoint="http://chat.local", + openwebui_api_key=None, + r2r_endpoint=None, + r2r_api_key=None, + ) + manager = StorageManager(settings) + + with pytest.raises(ValueError): + manager.build_multi_storage_adapter([StorageBackend.WEAVIATE]) + + +@pytest.mark.asyncio +async def test_storage_manager_search_across_backends_groups_results(document_factory) -> None: + settings = SimpleNamespace( + weaviate_endpoint="http://weaviate.local", + weaviate_api_key=None, + openwebui_endpoint="http://chat.local", + openwebui_api_key=None, + r2r_endpoint=None, + r2r_api_key=None, + ) + manager = StorageManager(settings) + + document_weaviate = document_factory(content="alpha", metadata_updates={"source_url": "https://alpha"}) + document_openwebui = document_factory(content="beta", metadata_updates={"source_url": "https://beta"}) + + manager.backends[StorageBackend.WEAVIATE] = StubStorage( + StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint="http://weaviate.local", + collection_name="primary", + ), + documents=[document_weaviate], + ) + manager.backends[StorageBackend.OPEN_WEBUI] = StubStorage( + StorageConfig( + backend=StorageBackend.OPEN_WEBUI, + endpoint="http://chat.local", + collection_name="secondary", + ), + documents=[document_openwebui], + ) + + results = await manager.search_across_backends( + "query", + limit=5, + backends=[StorageBackend.WEAVIATE, StorageBackend.OPEN_WEBUI], + ) + + assert results[StorageBackend.WEAVIATE][0].content == "alpha" + assert results[StorageBackend.OPEN_WEBUI][0].content == "beta" diff --git a/tests/unit/utils/__init__.py b/tests/unit/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit/utils/__pycache__/__init__.cpython-312.pyc b/tests/unit/utils/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..481ffeb Binary files /dev/null and b/tests/unit/utils/__pycache__/__init__.cpython-312.pyc differ diff --git a/tests/unit/utils/__pycache__/test_metadata_tagger.cpython-312-pytest-8.4.2.pyc b/tests/unit/utils/__pycache__/test_metadata_tagger.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..f5e51d2 Binary files /dev/null and b/tests/unit/utils/__pycache__/test_metadata_tagger.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/utils/__pycache__/test_vectorizer.cpython-312-pytest-8.4.2.pyc b/tests/unit/utils/__pycache__/test_vectorizer.cpython-312-pytest-8.4.2.pyc new file mode 100644 index 0000000..1c566ab Binary files /dev/null and b/tests/unit/utils/__pycache__/test_vectorizer.cpython-312-pytest-8.4.2.pyc differ diff --git a/tests/unit/utils/test_metadata_tagger.py b/tests/unit/utils/test_metadata_tagger.py new file mode 100644 index 0000000..6056a00 --- /dev/null +++ b/tests/unit/utils/test_metadata_tagger.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import json + +import pytest + +from ingest_pipeline.core.exceptions import IngestionError +from ingest_pipeline.utils.metadata_tagger import MetadataTagger + + +@pytest.mark.asyncio +async def test_tag_document_merges_and_sanitizes_metadata( + httpx_stub, + document_factory, +) -> None: + """Enrich document metadata using normalized fields from the LLM response.""" + + payload = { + "choices": [ + { + "message": { + "content": json.dumps( + { + "tags": ["AI", " Research "], + "category": " Technology ", + "summary": " concise summary ", + "key_topics": ["Topic", ""], + "document_type": " Reference ", + "language": "EN", + "technical_level": "Advanced", + } + ) + } + } + ] + } + httpx_stub.queue_json(payload) + document = document_factory(content="sample body", metadata_updates={"title": "Original"}) + + async with MetadataTagger(llm_endpoint="https://llm.lab", model="fireworks/glm") as tagger: + sanitized = tagger._sanitize_metadata( # noqa: SLF001 - exercising sanitizer directly for coverage + { + "tags": ["AI", " Research "], + "category": " Technology ", + "summary": " concise summary ", + "key_topics": ["Topic", ""], + "document_type": " Reference ", + "language": "EN", + "technical_level": "Advanced", + } + ) + enriched = await tagger.tag_document(document) + + assert sanitized == { + "tags": ["ai", "research"], + "category": "Technology", + "summary": "concise summary", + "key_topics": ["Topic"], + "document_type": "Reference", + "language": "en", + "technical_level": "advanced", + } + assert enriched.metadata["title"] == "Original" + assert enriched.metadata["description"] == "concise summary" + assert enriched.metadata["source_url"] == "https://example.com/article" + + +@pytest.mark.asyncio +async def test_tag_document_skip_empty_content(httpx_stub, document_factory) -> None: + """Return document untouched when content is empty.""" + + document = document_factory(content="") + + async with MetadataTagger() as tagger: + untouched = await tagger.tag_document(document) + + assert untouched is document + assert untouched.metadata["word_count"] == document.metadata["word_count"] + + +@pytest.mark.asyncio +async def test_tag_batch_processes_documents( + httpx_stub, + document_factory, +) -> None: + """Apply tagging to each document in order.""" + + first_payload = { + "choices": [ + {"message": {"content": json.dumps({"summary": "First summary"})}} + ] + } + second_payload = { + "choices": [ + {"message": {"content": json.dumps({"summary": "Second summary"})}} + ] + } + httpx_stub.queue_json(first_payload) + httpx_stub.queue_json(second_payload) + documents = [ + document_factory(content="First"), + document_factory(content="Second"), + ] + + async with MetadataTagger() as tagger: + enriched_documents = await tagger.tag_batch(documents) + + assert enriched_documents[0].metadata["description"] == "First summary" + assert enriched_documents[1].metadata["description"] == "Second summary" + + +@pytest.mark.asyncio +async def test_tag_document_raises_on_invalid_json(httpx_stub, document_factory) -> None: + """Raise ingestion error when the LLM response is malformed.""" + + payload = { + "choices": [ + {"message": {"content": "not-json"}} + ] + } + httpx_stub.queue_json(payload) + document = document_factory(content="broken payload") + + async with MetadataTagger() as tagger: + with pytest.raises(IngestionError): + await tagger.tag_document(document) diff --git a/tests/unit/utils/test_vectorizer.py b/tests/unit/utils/test_vectorizer.py new file mode 100644 index 0000000..5247e64 --- /dev/null +++ b/tests/unit/utils/test_vectorizer.py @@ -0,0 +1,112 @@ +from __future__ import annotations + +import pytest + +from ingest_pipeline.core.exceptions import VectorizationError +from ingest_pipeline.core.models import StorageBackend, StorageConfig +from ingest_pipeline.utils.vectorizer import Vectorizer + + +@pytest.mark.asyncio +async def test_vectorize_returns_stubbed_embedding( + httpx_stub, + vector_config_factory, + embedding_payload_factory, +) -> None: + """Verify vectorize returns embedding data from the stubbed client.""" + + config = vector_config_factory( + model="ollama/bge-m3:latest", + dimension=4, + endpoint="http://llm.lab", + ) + httpx_stub.queue_json(embedding_payload_factory(dimension=config.dimension)) + + async with Vectorizer(config) as vectorizer: + vector = await vectorizer.vectorize("synthetic content") + expected_url = f"{vectorizer.endpoint}/v1/embeddings" + + assert vector == [0.0, 1.0, 2.0, 3.0] + assert httpx_stub.requests[0]["url"] == expected_url + assert httpx_stub.requests[0]["json_body"]["model"] == config.model + + +@pytest.mark.asyncio +async def test_vectorizer_storage_config_uses_defaults( + httpx_stub, + embedding_payload_factory, +) -> None: + """Ensure storage-backed configuration falls back to default embedding settings.""" + + storage_config = StorageConfig( + backend=StorageBackend.WEAVIATE, + endpoint="https://storage.example/api", + ) + httpx_stub.queue_json(embedding_payload_factory(dimension=1024)) + + async with Vectorizer(storage_config) as vectorizer: + vector = await vectorizer.vectorize("repo content") + + assert len(vector) == 1024 + assert httpx_stub.requests[0]["json_body"]["model"] == "ollama/bge-m3" + assert httpx_stub.requests[0]["url"] == "http://llm.lab/v1/embeddings" + + +@pytest.mark.asyncio +async def test_vectorize_batch_emits_sequence( + httpx_stub, + vector_config_factory, + embedding_payload_factory, +) -> None: + """Validate vectorize_batch aggregates sequential stubbed embeddings.""" + + config = vector_config_factory( + model="text-embedding-3-small", + dimension=3, + endpoint="https://api.openai.example", + ) + httpx_stub.queue_json(embedding_payload_factory(dimension=config.dimension)) + httpx_stub.queue_json(embedding_payload_factory(dimension=config.dimension)) + + async with Vectorizer(config) as vectorizer: + vectors = await vectorizer.vectorize_batch(["doc one", "doc two"]) + expected_url = f"{vectorizer.endpoint}/v1/embeddings" + + assert vectors == [[0.0, 1.0, 2.0], [0.0, 1.0, 2.0]] + assert httpx_stub.requests[0]["url"] == expected_url + assert httpx_stub.requests[1]["json_body"]["input"] == "doc two" + + +@pytest.mark.asyncio +async def test_vectorize_detects_dimension_mismatch( + httpx_stub, + vector_config_factory, + embedding_payload_factory, +) -> None: + """Raise VectorizationError when backend returns unexpected vector length.""" + + config = vector_config_factory( + model="ollama/bge-m3:latest", + dimension=4, + endpoint="http://llm.lab", + ) + httpx_stub.queue_json(embedding_payload_factory(dimension=3)) + + async with Vectorizer(config) as vectorizer: + with pytest.raises(VectorizationError): + await vectorizer.vectorize("truncated embedding") + + +@pytest.mark.asyncio +async def test_vectorize_rejects_empty_text(vector_config_factory) -> None: + """Reject empty payloads before issuing HTTP requests.""" + + config = vector_config_factory( + model="ollama/bge-m3:latest", + dimension=4, + endpoint="http://llm.lab", + ) + + async with Vectorizer(config) as vectorizer: + with pytest.raises(VectorizationError): + await vectorizer.vectorize("") diff --git a/typings/__init__.py b/typings/__init__.py index dc6375a..1efb184 100644 --- a/typings/__init__.py +++ b/typings/__init__.py @@ -10,4 +10,4 @@ class EmbeddingData(TypedDict): class EmbeddingResponse(TypedDict): """Structure for OpenAI-compatible embedding API response.""" - data: list[EmbeddingData] \ No newline at end of file + data: list[EmbeddingData] diff --git a/typings/__init__.pyi b/typings/__init__.pyi index dc6375a..929db06 100644 --- a/typings/__init__.pyi +++ b/typings/__init__.pyi @@ -2,7 +2,6 @@ from typing import TypedDict - class EmbeddingData(TypedDict): """Structure for embedding data from API response.""" embedding: list[float] @@ -10,4 +9,4 @@ class EmbeddingData(TypedDict): class EmbeddingResponse(TypedDict): """Structure for OpenAI-compatible embedding API response.""" - data: list[EmbeddingData] \ No newline at end of file + data: list[EmbeddingData] diff --git a/typings/__pycache__/__init__.cpython-312.pyc b/typings/__pycache__/__init__.cpython-312.pyc index a0d5685..903cd04 100644 Binary files a/typings/__pycache__/__init__.cpython-312.pyc and b/typings/__pycache__/__init__.cpython-312.pyc differ diff --git a/typings/dotenv.pyi b/typings/dotenv.pyi index f2c521a..1f3e5ed 100644 --- a/typings/dotenv.pyi +++ b/typings/dotenv.pyi @@ -4,5 +4,4 @@ from __future__ import annotations from pathlib import Path - -def load_dotenv(dotenv_path: str | Path | None = None) -> bool: ... \ No newline at end of file +def load_dotenv(dotenv_path: str | Path | None = None) -> bool: ... diff --git a/typings/httpx.pyi b/typings/httpx.pyi index b842003..32fbc0a 100644 --- a/typings/httpx.pyi +++ b/typings/httpx.pyi @@ -2,9 +2,6 @@ from __future__ import annotations -from typing import TypedDict - - class Response: """HTTP response object.""" @@ -36,4 +33,8 @@ class AsyncClient: json: dict[str, object] | None = None ) -> Response: ... - async def aclose(self) -> None: ... \ No newline at end of file + async def aclose(self) -> None: ... + + +# Make AsyncClient available for import +__all__ = ["AsyncClient", "Response"] diff --git a/typings/prefect.pyi b/typings/prefect.pyi new file mode 100644 index 0000000..6ed4156 --- /dev/null +++ b/typings/prefect.pyi @@ -0,0 +1,114 @@ +"""Type stubs for Prefect to fix missing type information.""" + +from typing import Any, Awaitable, Callable, TypeVar, overload +from typing_extensions import ParamSpec + +# Prefect-specific types for cache key functions +class TaskRunContext: + """Prefect task run context.""" + pass + +P = ParamSpec("P") +T = TypeVar("T") + +@overload +def task( + *, + name: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + version: str | None = None, + cache_key_fn: Callable[[TaskRunContext, dict[str, Any]], str] | None = None, + cache_expiration: Any = None, + task_run_name: Any = None, + retries: int = 0, + retry_delay_seconds: float | int | list[float] | None = None, + retry_jitter_factor: float | None = None, + persist_result: bool | None = None, + result_storage: Any = None, + result_storage_key: str | None = None, + result_serializer: Any = None, + cache_result_in_memory: bool = True, + timeout_seconds: int | float | None = None, + log_prints: bool | None = None, + refresh_cache: bool | None = None, + on_completion: list[Any] | None = None, + on_failure: list[Any] | None = None, + retry_condition_fn: Any = None, + viz_return_value: Any = None, +) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]: ... + +@overload +def task( + *, + name: str | None = None, + description: str | None = None, + tags: list[str] | None = None, + version: str | None = None, + cache_key_fn: Callable[[TaskRunContext, dict[str, Any]], str] | None = None, + cache_expiration: Any = None, + task_run_name: Any = None, + retries: int = 0, + retry_delay_seconds: float | int | list[float] | None = None, + retry_jitter_factor: float | None = None, + persist_result: bool | None = None, + result_storage: Any = None, + result_storage_key: str | None = None, + result_serializer: Any = None, + cache_result_in_memory: bool = True, + timeout_seconds: int | float | None = None, + log_prints: bool | None = None, + refresh_cache: bool | None = None, + on_completion: list[Any] | None = None, + on_failure: list[Any] | None = None, + retry_condition_fn: Any = None, + viz_return_value: Any = None, +) -> Callable[[Callable[P, T]], Callable[P, T]]: ... + +@overload +def task( + fn: Callable[P, Awaitable[T]], +) -> Callable[P, Awaitable[T]]: ... + +@overload +def task( + fn: Callable[P, T], +) -> Callable[P, T]: ... + +@overload +def flow( + *, + name: str | None = None, + description: str | None = None, + version: str | None = None, + flow_run_name: Any = None, + task_runner: Any = None, + timeout_seconds: int | float | None = None, + validate_parameters: bool = True, + retries: int = 0, + retry_delay_seconds: float | int | list[float] | None = None, + persist_result: bool | None = None, + result_storage: Any = None, + result_storage_key: str | None = None, + result_serializer: Any = None, + cache_result_in_memory: bool = True, + log_prints: bool | None = None, + on_completion: list[Any] | None = None, + on_failure: list[Any] | None = None, + on_crashed: list[Any] | None = None, + on_cancellation: list[Any] | None = None, + on_running: list[Any] | None = None, +) -> Callable[[Callable[P, Awaitable[T]]], Callable[P, Awaitable[T]]]: ... + +@overload +def flow( + fn: Callable[P, Awaitable[T]], +) -> Callable[P, Awaitable[T]]: ... + +class Logger: + """Logger interface stub.""" + def info(self, msg: str, *args: Any, **kwargs: Any) -> None: ... + def warning(self, msg: str, *args: Any, **kwargs: Any) -> None: ... + def error(self, msg: str, *args: Any, **kwargs: Any) -> None: ... + +def get_run_logger() -> Logger: ... \ No newline at end of file diff --git a/typings/prefect/blocks/core.pyi b/typings/prefect/blocks/core.pyi new file mode 100644 index 0000000..44a878a --- /dev/null +++ b/typings/prefect/blocks/core.pyi @@ -0,0 +1,19 @@ +"""Type stubs for Prefect blocks core module.""" + +from typing import Any, ClassVar, TypeVar +from typing_extensions import Self + +T = TypeVar("T", bound="Block") + +class Block: + _block_type_name: ClassVar[str] + _block_type_slug: ClassVar[str] + _description: ClassVar[str] + + @classmethod + async def aload(cls: type[T], name: str, validate: bool = True, client: Any = None) -> T: ... + + @classmethod + def load(cls: type[T], name: str, validate: bool = True, client: Any = None) -> T: ... + + def save(self, name: str, overwrite: bool = False) -> None: ... \ No newline at end of file diff --git a/typings/r2r/__init__.pyi b/typings/r2r/__init__.pyi index 792a0f9..6099d18 100644 --- a/typings/r2r/__init__.pyi +++ b/typings/r2r/__init__.pyi @@ -1,7 +1,7 @@ from __future__ import annotations from collections.abc import Iterator, Sequence -from typing import Any, Generic, Protocol, TypeVar +from typing import Generic, Protocol, TypeVar _T_co = TypeVar("_T_co", covariant=True)