From 2d01228f2498653e11cef679c41b066205557a29 Mon Sep 17 00:00:00 2001 From: Travis Vasceannie Date: Fri, 19 Sep 2025 06:56:19 +0000 Subject: [PATCH] updates --- .claude/settings.local.json | 3 +- .gitignore | 100 + .vscode/settings.json | 36 +- basedpyright.json | 24 +- chat.json | 23483 +++++++++++++++- ingest_pipeline/automations/__init__.py | 62 + .../cli/__pycache__/main.cpython-312.pyc | Bin 25803 -> 27375 bytes ingest_pipeline/cli/main.py | 44 +- .../cli/tui/__pycache__/app.cpython-312.pyc | Bin 13652 -> 13706 bytes .../tui/__pycache__/styles.cpython-312.pyc | Bin 47126 -> 47261 bytes ingest_pipeline/cli/tui/app.py | 17 +- ingest_pipeline/cli/tui/layouts.py | 4 +- .../__pycache__/dashboard.cpython-312.pyc | Bin 33571 -> 34012 bytes .../__pycache__/dialogs.cpython-312.pyc | Bin 17213 -> 17158 bytes .../__pycache__/documents.cpython-312.pyc | Bin 18756 -> 18900 bytes .../__pycache__/ingestion.cpython-312.pyc | Bin 24007 -> 24014 bytes .../__pycache__/search.cpython-312.pyc | Bin 14949 -> 15103 bytes ingest_pipeline/cli/tui/screens/base.py | 108 +- ingest_pipeline/cli/tui/screens/dashboard.py | 25 +- ingest_pipeline/cli/tui/screens/dialogs.py | 27 +- ingest_pipeline/cli/tui/screens/documents.py | 7 +- ingest_pipeline/cli/tui/screens/ingestion.py | 6 +- ingest_pipeline/cli/tui/screens/search.py | 10 +- ingest_pipeline/cli/tui/styles.py | 8 +- .../utils/__pycache__/runners.cpython-312.pyc | Bin 6905 -> 6961 bytes .../storage_manager.cpython-312.pyc | Bin 24918 -> 24918 bytes ingest_pipeline/cli/tui/utils/runners.py | 21 +- .../cli/tui/utils/storage_manager.py | 9 +- .../cli/tui/widgets/firecrawl_config.py | 111 +- .../cli/tui/widgets/r2r_widgets.py | 15 +- ingest_pipeline/config/__init__.py | 50 +- .../__pycache__/__init__.cpython-312.pyc | Bin 1790 -> 3099 bytes .../__pycache__/settings.cpython-312.pyc | Bin 5689 -> 9806 bytes ingest_pipeline/config/settings.py | 75 + .../core/__pycache__/models.cpython-312.pyc | Bin 8828 -> 9557 bytes ingest_pipeline/core/models.py | 29 +- .../__pycache__/ingestion.cpython-312.pyc | Bin 25761 -> 30027 bytes .../__pycache__/scheduler.cpython-312.pyc | Bin 3309 -> 4204 bytes ingest_pipeline/flows/ingestion.py | 206 +- ingest_pipeline/flows/scheduler.py | 46 +- .../__pycache__/base.cpython-312.pyc | Bin 1827 -> 3054 bytes .../__pycache__/firecrawl.cpython-312.pyc | Bin 22870 -> 25327 bytes .../__pycache__/repomix.cpython-312.pyc | Bin 22603 -> 22522 bytes ingest_pipeline/ingestors/base.py | 33 + ingest_pipeline/ingestors/firecrawl.py | 71 +- ingest_pipeline/ingestors/repomix.py | 35 +- ingest_pipeline/py.typed | 0 .../storage/__pycache__/base.cpython-312.pyc | Bin 6026 -> 7680 bytes .../__pycache__/openwebui.cpython-312.pyc | Bin 31115 -> 31900 bytes .../__pycache__/weaviate.cpython-312.pyc | Bin 43285 -> 43251 bytes ingest_pipeline/storage/base.py | 46 +- ingest_pipeline/storage/openwebui.py | 118 +- .../r2r/__pycache__/storage.cpython-312.pyc | Bin 39945 -> 40079 bytes ingest_pipeline/storage/r2r/collections.py | 27 +- ingest_pipeline/storage/r2r/storage.py | 43 +- ingest_pipeline/storage/weaviate.py | 11 +- .../metadata_tagger.cpython-312.pyc | Bin 11847 -> 15565 bytes .../__pycache__/vectorizer.cpython-312.pyc | Bin 7723 -> 7724 bytes ingest_pipeline/utils/metadata_tagger.py | 105 +- ingest_pipeline/utils/vectorizer.py | 2 +- logs/tui.log | 26 + prefect.yaml | 154 + pyproject.toml | 14 +- pyrightconfig.json | 45 + repomix-output.xml | 3448 ++- tests/__init__.py | 0 tests/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 152 bytes .../conftest.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 22992 bytes tests/__pycache__/conftest.cpython-312.pyc | Bin 0 -> 22878 bytes .../__pycache__/openapi_mocks.cpython-312.pyc | Bin 0 -> 41440 bytes tests/conftest.py | 545 + tests/openapi_mocks.py | 807 + tests/unit/__init__.py | 0 .../unit/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 157 bytes tests/unit/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 194 bytes ...test_main_cli.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 10856 bytes tests/unit/cli/test_main_cli.py | 147 + tests/unit/flows/__init__.py | 1 + .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 197 bytes ...ngestion_flow.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 9455 bytes ...est_scheduler.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 7574 bytes tests/unit/flows/test_ingestion_flow.py | 131 + tests/unit/flows/test_scheduler.py | 72 + tests/unit/ingestors/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 167 bytes ...rawl_ingestor.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 4878 bytes ...omix_ingestor.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 8411 bytes .../unit/ingestors/test_firecrawl_ingestor.py | 64 + tests/unit/ingestors/test_repomix_ingestor.py | 85 + tests/unit/storage/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 165 bytes ..._base_storage.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 6829 bytes ...est_openwebui.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 18917 bytes ...t_r2r_helpers.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 27342 bytes ...viate_helpers.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 15886 bytes tests/unit/storage/test_base_storage.py | 102 + tests/unit/storage/test_openwebui.py | 136 + tests/unit/storage/test_r2r_helpers.py | 343 + tests/unit/storage/test_weaviate_helpers.py | 140 + tests/unit/tui/__init__.py | 1 + .../tui/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 194 bytes ...hboard_screen.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 16098 bytes ...orage_manager.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 12595 bytes tests/unit/tui/test_dashboard_screen.py | 215 + tests/unit/tui/test_storage_manager.py | 172 + tests/unit/utils/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 163 bytes ...tadata_tagger.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 9001 bytes ...st_vectorizer.cpython-312-pytest-8.4.2.pyc | Bin 0 -> 10964 bytes tests/unit/utils/test_metadata_tagger.py | 126 + tests/unit/utils/test_vectorizer.py | 112 + typings/__init__.py | 2 +- typings/__init__.pyi | 3 +- typings/__pycache__/__init__.cpython-312.pyc | Bin 835 -> 835 bytes typings/dotenv.pyi | 3 +- typings/httpx.pyi | 9 +- typings/prefect.pyi | 114 + typings/prefect/blocks/core.pyi | 19 + typings/r2r/__init__.pyi | 2 +- 120 files changed, 30801 insertions(+), 1255 deletions(-) create mode 100644 .gitignore create mode 100644 ingest_pipeline/automations/__init__.py create mode 100644 ingest_pipeline/py.typed create mode 100644 prefect.yaml create mode 100644 pyrightconfig.json create mode 100644 tests/__init__.py create mode 100644 tests/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/__pycache__/conftest.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/__pycache__/conftest.cpython-312.pyc create mode 100644 tests/__pycache__/openapi_mocks.cpython-312.pyc create mode 100644 tests/conftest.py create mode 100644 tests/openapi_mocks.py create mode 100644 tests/unit/__init__.py create mode 100644 tests/unit/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/cli/__init__.py create mode 100644 tests/unit/cli/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/cli/__pycache__/test_main_cli.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/cli/test_main_cli.py create mode 100644 tests/unit/flows/__init__.py create mode 100644 tests/unit/flows/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/flows/__pycache__/test_ingestion_flow.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/flows/__pycache__/test_scheduler.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/flows/test_ingestion_flow.py create mode 100644 tests/unit/flows/test_scheduler.py create mode 100644 tests/unit/ingestors/__init__.py create mode 100644 tests/unit/ingestors/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/ingestors/__pycache__/test_firecrawl_ingestor.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/ingestors/__pycache__/test_repomix_ingestor.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/ingestors/test_firecrawl_ingestor.py create mode 100644 tests/unit/ingestors/test_repomix_ingestor.py create mode 100644 tests/unit/storage/__init__.py create mode 100644 tests/unit/storage/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/storage/__pycache__/test_base_storage.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/storage/__pycache__/test_openwebui.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/storage/__pycache__/test_r2r_helpers.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/storage/__pycache__/test_weaviate_helpers.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/storage/test_base_storage.py create mode 100644 tests/unit/storage/test_openwebui.py create mode 100644 tests/unit/storage/test_r2r_helpers.py create mode 100644 tests/unit/storage/test_weaviate_helpers.py create mode 100644 tests/unit/tui/__init__.py create mode 100644 tests/unit/tui/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/tui/__pycache__/test_dashboard_screen.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/tui/__pycache__/test_storage_manager.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/tui/test_dashboard_screen.py create mode 100644 tests/unit/tui/test_storage_manager.py create mode 100644 tests/unit/utils/__init__.py create mode 100644 tests/unit/utils/__pycache__/__init__.cpython-312.pyc create mode 100644 tests/unit/utils/__pycache__/test_metadata_tagger.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/utils/__pycache__/test_vectorizer.cpython-312-pytest-8.4.2.pyc create mode 100644 tests/unit/utils/test_metadata_tagger.py create mode 100644 tests/unit/utils/test_vectorizer.py create mode 100644 typings/prefect.pyi create mode 100644 typings/prefect/blocks/core.pyi 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 74efecba560ff7e2f1aa9fb5fd90e113bd4cda98..c0fabe5b58a567213af75ae7f35a807c2495aa91 100644 GIT binary patch delta 7721 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 delta 6474 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 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 691b0822116f4ffec05d65b814063fe032b942a3..fd540df85ddc9dbfa513180552b4e4803aaa79f4 100644 GIT binary patch delta 1686 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 C9eu|D diff --git a/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc b/ingest_pipeline/cli/tui/__pycache__/styles.cpython-312.pyc index 53a086536f9938c1869d633dd46f4e19a32f6c58..45304920578ed54eae4bc51faf28b88ede5cb054 100644 GIT binary patch delta 982 zcmY+COGs2v7{|YJA9LqDnlnDikd7-x(j(2lw|q|L5L&&Uuhkrq&eKvdd)$ zwA{yc9n1GzWhA*!T}lpmSuu4ngSx3NY@x+NTBt;U2B0NFKuhNosQ?I-(lWZ8?x5w= zPb(hjB0+W+P&f006*;B3-w@{2I#R_a7a;)SU=!}a5|K&{ zHtR82&RYVgviUadjye9VCzQF3IRfVY8g)xkm=I&z{8UbudKJXnn=1r>SN5q?qk5;c zPfKW#qRq}`5~h?n{6J|0E&$d0)TCpo86(5qg6I0+i!S^p7R#^5Z>S+ql} z_8;2WiK2|&-pSf{M(=6kQI;{HEY=o_ao&YJaV+Kc@(4z}RiZ=A6UJkrJ5_CuV|GRZ zh>nYy)X6<7&Z4p76TTVsgEO$=@{XOJ2)=g(zBoOjjdvPHnzA~$cm4Wd0jKZ&_1gYA z1KxPbB!lJ1CBs&?UtKCFt*cR&YIKa-arG^eXCW;YMvMc45TOuKC<&?5K~<(v<*EUI z+ognY%ZBujPA$wrHD+LL;8|Pqu|lmF+o%@6hC*$a*s4F?cS4fsZ#SMwH+ri? zC2q0M>+R22x-UgodyF@uisJ_ZuaQVfq$F-5G8(;tGxDGLh=fwMKfj=th(4ae3+Q&*5P44nM|)fX|R&1^xlQD zDw!5HpMQ2t%Y`Q;rbO3^^JG@MeNj&;gnRLtzSy}f`=TPx8>a{vG9`p0p{TGxBvhE delta 871 zcmYL{Ur19?9LLYO_uTDn^EzpB|8fZ`%AkBQ3e1Nrv$Uj$EEiPU&e_z4jqGJR2o+N-_v;CtAuUW0m??4_5WbEei0?&tUYo!=ig_jd~W(#w*h?P{7C zaC^|aJ2?A7bHj;^78mT$h!Iy~dl=!`Jn}mv&<(I753qAh64e0so!Eul*n^MbI_!O| z_+7Y~Si=%Nkw+{6n^DT{YbdV5w`o^j=&+~KsBp^sD_>;Msbz?mmUY&uX(#6RzH2_#zS7ME1@U}P+f@yXq?cfI5JWarX)V7o4S%A5=#t? zj_AhdXe2T;644b*f{!9TMVAAy7`@Bu>7ZzWgi`iyy2U)kqgx1o_zc^d?s4on+)pno zbe9Z3t9jl#*Sm1-_cx23U47h|J>Ld)tTw^M8o@SGXT7}B;p+6rA3X}!AwKG#@sSlP z_jC6O5a5Rx_$3VeGB#s55B>bEM8OI%@GTnn7L64xVI$Y-p;oyzacw3BJF$c%Hy_5A zXyV6JDamJH-rRL5B(eQTJ6p*0zy#aNHF5sQ-DWg*j0I+X*(P|8Zs6A^BbX+5K*x&r z@;@NV6uX|R3RjwS%2|z8iJT@N)=8}beF8>-Qvw-*tiUXXj_^<@LB+eE&jjoOwE_-- z3FNrTuSO)~3tUClv-h${_mSo^luyuxGg*5W5w TaQl 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 83cf24d79598283fa81b3a65346e2f6a7b80181f..086f416443f48e7f449f5769a7501d04a13d6922 100644 GIT binary patch delta 3885 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 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 aa93382c272f946c35b19a48fac0c1583702df90..6b9b049d0bc4940ab5c6972ee2094071df43632d 100644 GIT binary patch delta 4504 zcma)9dr(x@8NX-u?k=!*-!Iq&c99jmf(Qsf5L8fBtpt2Ek!>2X?7awPmnCNxQG&rl zW1elWr&H5OY_YGzG0kM+Op`QClcuAYHtkdrGuRu`ai-0GZKI=|bey*7_nl>VXgiZL zv%mA5@B7a4ey@G&7&&%~Sl_o;3Iu3@kA64MS9{!ANQC3df)hkgh$1EoNn_N=X+y%4 z%!}r6+L*{s7DNk@=BSyEO$jM!iCU7@sFjcN61Jp0YEL?%j-)f{OuC}3q&wzvtOcq6ph@gs8QY6Ka@FxS&cmRZwVoek$OQI!2&~;_7$@SjIL#N#n~?^9^l$Q>V@78y$^cFozx zXSi2iYe8ibf*pZt<{@Q9kN{2-Dnpkh03lj=_FCT0i4*!pBC4eL!SVbmqNm@ZIFT&yZ=n1Xg{X?)M!`=2{`sizN3s42yAaqU z+Kq>|Fu8colB%3qR~)FhhMyMO^_v3wVewJcT2dt2!8{98v*J)CFi+~i z3Jzl17vkpWP+VZ1XqFC;qFFkjFNEI3&C?C$>DL16svKY!OX}_1qP(yrH%S^`*3ueX zT;$doFWs??e?bFXh8q0Ijv_U0!v=1BbTK#kD)_D;Xz;c9Z1zQIkC9(|<=K0Kr-^YD zvTK;7tjSY{TyUgVIB~`AOqCMa<2hb>q5yX0~Ig zhkaJz632yoRpHK{%w(peORC#<>7VP7fgk}^;zu2vl5q<8M651JZ< zr$2z-@^T@wBzKFjK+6spXV2^EwvQX16He+Bo8oxMnM;dA=z$kHWPHf@bZxdDC8NRt zeUC6o%7w+d#iG8V5 zLe^=z>KeKqVKtRy z#iKx`O#m2Fpk>1fu0B=;@vO@DTU0p0zE>HXD9#UGws|KrGd_9RC%@SAk*(qN@Jw^( zbaUrS^X?CucV9IMf#8h4YT92l)3GCqMoT4;c?dkhKB+DuBKy2LHK7#oGRy*bE?+HBB-IRg&blDaNsz&8kijtD z*`c?Bj+iagpa^rSc9j>EP;%-Z_hG^2yp4-_(gk_asOZMPhGum!PX^qxiPAHhD7`#U z`Y=&4Z_Rxqz(#B8$V#@Ou?Pw{7yG!Trk;C{xF9V~!Nut*0Eo`KmRbRH)}iL|-}>4T za+7_%wgSqgqqT*E)UcCvn<|j~~; z>uWu1sv*w^|2Twn6MMPAyZkR^=VbkfHOJRnD5<$P{nR$sVobU+;n z#S`%7K&jRo3YRXrmwv%TUWI)YU4&h|qq4z?=f^L5Sh!&nB-fX_0T>SXHk)V-uw#v# zg@?@<3}U>&7nr@%BZLh%dbfem>AJ=WcMiq}I#YW?ql3y`H4_?2WkUU_ks$@FVG1VB zzrQB!92v?CCe;o~Q*`lxKW}U>!XH8*y^+T z(_+@#Ut^Co1xY1)p=lKazL}<7ywaOpWK;8xE8c+lST6C!-ozDuf$$bUS_H^ObY@$- zk}PAbYd3?BJPLI16l!6O#s2|m#*twqmQi6L?q$Db(=9I8_S2RIQq2ObTeI5|W;eUp zL#;jD`K+?gD%sy!SCK|mvMvDIG_4a!gjbv~*k;U!58n@|E)?4j886m{RHivZ?*nOs z@@%K$u1=G9tVQb{S>H(3vNzX9CU}_R;kodCh3Blp^0*9z(3gtKXHkzo-$|r+V1Eax zcM;x0_zlAQ2;sws4+AU=*55}a!rxKwd-n2%NH#dF8J4&f?6caQ=68r z#L@$OsTfr#){HQoD~!E3xe$S;E4;~RsH>4t32-y71r{1fsO#t@_D;Jm{}Unfsj&1D sp`2Z5udBS5knmK+i|$iZHw2__wCi^gvX4yOKNX(#F8hbTM|`>e0)tcwtN;K2 delta 4605 zcma)A4Qv$072dhsz5gGd@6W#T&pEa+XT$jij19(sF*Xp4fq-bZG`XH{7i_cVGqdLy zJ4b99LQs-ckWow8G=j8h6BjC_K~>tcX-gYYscBN&phB(`BBhb4R*I-dn<{FXzBgwZ z&TNRrYOod`0BB&CzD-(9bp_QacW2Mk(SDac|sytRs z1cR_qP+Ug@r9drE+{Li&Ga{~D5Mp88R|tKcPxV#sJ}>n7RM&!Wca6Ct{_&O#0|_m? zFRUl2st)OFHEHQIycL5gDUu@Ym5vE} ziBfRH9Hj8wqFieqTFfUH~G7n#+|-SdY`tqX6H3y{enfuf1GV z!<0{Fmm|OSuU9i?wU=LU1wL>sy5L&$P&4*Wem#oO>(nhOeH&z1i*niu#dlZQ+XCEs z#rAfAo2t^cd$@}_bDLkd=pzUNmUd3KxRwB%7A);9VcJO$dYtXch3RFaeR<;kons;? zqeAx>l!V{xw#PA%QKDhm444IA)RfL3U;#57E8j$#4?#Tw+|E15qtXNUdNhd;& z^5gvfzvQ9rJ_CKsXyykqp;|TzYS~DCZY`#fKHXPKpPIR{M`uvq4a*!Qjt0sd4$AKvY9RVZzd2QgkX-s7u15!gegtz4s5mn7 zVRXECWe8p)u;PQAiUHl%R2)AZSO-Vj%s_7OZXTxO22WKg4z)#Vy|LiHGT2CGTvc>% z@KRia!lSVU3XeuR6dsKxF;TAv^z_<1fD;}w@Fprrqgg#sB_du!){A7KmLPW-awqkq z(JD@6B6l(yxsxX3PMQf)tPI*Mjn%?r8EGunOfFO-UM(QE#;RRUgvm8LG<$<1fWJY6 z^rc+QyE$Nk?I1tP1? zOX$x=(-#F=-##*?0f(`}J;Ge30^^!}WE1806cIZs-NyQJvazTde*yk8;HHQlR2T>M zcukH}6F5>WV_Mlwf3#;CIE=7&QAgI-I}AJTZssNrfWEYwD6wwVSFp5^*y486NA{wd zOzo}C{~@}GD(xmF?dC5P=Co|z)p#DbN5dL$j|LyOM?)^u<<)?mF7^PN5X``vu#tv5 z^@LqS+)Wzu$OPOK0T*t4fa0?>tQ01ekcOq2i5fNHD+Q#l(Kfo_(#P0n6DDmc#N7h4 zz2QPomw0;rH4;agl~~Gf9$n2Hocp)z{@eh1OhkMFNqJJ*|Xw(3zRZC+_VXA}DyY@P0117YH}2fXS} zkeo^HeV_>D^Xvnw$j6CaKX8Cp%2gcK%n|MhI4Z!|#F!xckp3jkPVakgHGTg)KuPwIeQ)%`R=Vou$OvoC z6{Q*Q=2ZoT^j++WywXm}i5cjO)k+_JsMzmOXmBr>l{)UmjPo*UI43~z(}!&ObhfR~ zyhm;NbZdpS{ip-4a*WwA(>xtHOviIaE%S6Jv|rwYW*E1@!dPh57HyW~pQlFYqIbMd zpS~qh)I2{vU$r9ltMN*61-riaBZIF){7J3xvWJUIg{g_N_+yq!62lLV5^{z9?psx0 ziMB^t0ro$#HExl1f#}yqq@BoR=8?7~Wb;Vpv- zKHka^Km{CVtvDV0g&?VhOMp>nI4N$448t!?k{@T54*;at{Mm9TRFDs-B^leY(gpd~ zb#NK$i}|qmY-2?X!^eTFFqIxP=fiJjumb?DYr_Lkc_%dK1pR4Ee){~Qd2!Yi>vXQc z0b}FrtwXAhWs3TX@RHC7l~K+$U?U6gpW<&$eY@H7@mk2(ShCGJVEbE-Eq{lACF`tH zwW5S>eyr?fLt%woYE{zxp{CTj=@HjRe}dNmmkh<<&ibBcXzqu(&eR*{A%Mp_0FgFh}Bp--GB&iXx<_aRsOXB|f>KIC%gnG==f Rd-Y@^Ip+Q&#}KCEe*rIFVgmpG delta 7755 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 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 64acc1a45d6a0758cea4697080d284f75373663a..81989d6997df438ca8414e5b8f62470acb8aafb7 100644 GIT binary patch delta 6724 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 Z_}m$1)UK$xC~y6lL6Oyc^Lm+DMgS0b7mok{ 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 0000000000000000000000000000000000000000..72ead4f5755179bb69f70f52af951f0678e37891 GIT binary patch literal 152 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 V;s(XVB|osaIZapHC5S05{|9bp$u0l@ literal 0 HcmV?d00001 diff --git a/tests/__pycache__/conftest.cpython-312.pyc b/tests/__pycache__/conftest.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54967955d824a90162f93a2329afe4b480873dab GIT binary patch literal 22878 zcmd6PdvF`andb~Jc!LB;f^Sj+MN$;?pq`d&y{xz77iCNGBXDFG!VE>o0Kv?FmI#wu zOzfyaJx}fBfa(@?C=Pd-^aR2M5HtBY-Xm zvLK5|AtgpckziZWma<3e47Mkwlq2F`u#|MBToG5w9dWa7N79q>M!YFc#KXRwNngqz z@iW+!tV&f!su}D~22#ODFjW(&N!3PbS)3`ZkDVd z?$pZ2%2ZFJM--G*O0Tkdz}AdmN7mQ`#jmV=N8~s-^Sp_a()%3)JoEWdoC#Hmi>$W^ zhXpzKnjqIGHSgGY-pB^D55UTG<_eaSM`cDBkrmNGl;tIRfem8Hzi`z+IIDYNUo$~-8q zwv^d@pJmoq$~=6ZW!75C>{+tRIt#8JlKU)W_TEn!kFfBcyj@;zsbk+?RL2f^gQbrB zpS2Eor@YZp{=jE0zf0a^DSz;O%FDau&6YBUM!mfc|B1w)SJZv6p`mmpmPw?CG<-T_ z0X=ekz<)yJQ>q8-7%0*k}~4#PoLPYyOE$|5-G|rJsHak;vG1i zNDU{I{#Z)UhGTK1*RH#tRMVMsJe|}%Cq{=A`9LC`(WQ7y%OJmdL`@FIR88rpMvr7N z!%wTpUZ?IpkdBX}l%b3sG@&PyOpLms2Ob$Zt!UKc5d>q(73U5h z_L86o5m6Q*wx_YMMeMRok!1T$q%cTgkOOhfBIII_8*v_jKun)rb&SZSWsWKFv?^zp z?T-y-MpQ)(A31U2$*{^3!^5%BWI87I>2@un>duss8BEK%9Td3NB!ei=BYJH(ed-L* zbuv5_?oSUXy623R9*Uky%cHszgo>rKdsRG(nFCCbRJ8tHhwhF>hd}sfRQE=sskA(j zB-kH~eqkh*G*VpAsGN=?IuMOocrO~gCd?6I*`puXWzbva72pa+1~f#A!+d(x^q{=ln`F1X z=#_3yKuUZXgm^@Y#?!JAH}nN03If4kIdQH9z$IZ!I76{z&vD_J2mw^LApRPJSyPe& z8kG#I>k;Y_F+ny|HiSNkeGuuIAHjr>57fOmc7ANy-OBK~pc|ydv2IZFOLapQUxKos zilYKs$Zc52?HNP;5Cl^t7#D5Am?%q?hw^RVJ2rDs|B~42=uce0Kq?b;EBf+!8Cey` zAd@f=5SK*9q%eT?Uh_w!N#UFraU2(Vo%gntV?&K4G$jh^ML8|{b|%yDSW?^FcLE$r zl6X*6)2g}>t?DjAD0R1LOgi!6Ksl;}I-=She0)p1BxLaB&w1OJ@CML1CYr+}?Q%?r z7R$3QAe%pm&dToj3rF5(HoPCTXv+|MG$Gs$)?akZ1jAPvXM?@@<((I#Z~F7$t_#w) zsu&mbO6o!6RKo~#r>bCH8`343ntGk;DoW|4U^NA6DWERuRYw2gN-`OZg0l}HmT~kR z&e2s1Y4R|V=}#lQ`=c)l`T7+(|B8G=XU^aGp=7V>zAGT$)5W96l3i}#fXl8zgbklf zWCp{W_TgTex()es&%twXWti3~#1M{EDy|W&!iI1K%NZvd{xk$rMH&SVI$z+HuXV=P zIu-wwuOsiTotIpm?#qwN3wYhF6#{M3%XZzAZ|&}%+1)?A>*#dbley}rrrl4m!Sth7 zhf$BZ8NoFJJ_5McU5?x}l#OAS2BwmXsE<>yjsiB3(p;f#GJ}l(G@6vZEc{N|ai^;8 z)uZ{wmWjtM)a9g>IZ1T1%{!|c$3$M1iY<^*2@!b2xl@qePnvqM5;~Z~IsqB0Bc@{y zzPuC<70Ic%uKP@JiMVAy(yCaQ>WD`U;48?!+%o;ExolskIF^6O%jMeFoDr`amg_LV z`^wYJZ@EJbA>D6&m%rr|ay`0za1sn3!vJfcC%NDPJ9a@=>Xf$5LWm+|jO-DA#j+P%7DLd!hAKw&vavmjGoG~Giw z;5n*5ccjk_DQdqlVUnVm9?4*N3dqVpY$Tb94#Y^W8_iaiv=^u_Cw14L5|b5G(~W7v zeJX~T8F~)pxo5x;>gOnU9zk)9a^izH&PN>gH2_$nL`^ z#!Kf!%_a&7!Q?jNGdNigGN_$DV-B8AE>7aeM}1(=m=j;cI!A?GTfZ7X3WV}0ywu|q z&_HyFM$U&5rbrr-`ZNM$rTL*Oe`(+>lP*RzH*$rI$1=w^S%^%;D9+u~thNWigivS@ zS~@4CuT|%p!gy8X8<*kbzunk6b@*?dxT5A7SF`V?)~O?Z(|^^LYg)sy)wfJaZ+My5 zFIx1l+K6+rsGjhDrigDu7*7dfTFnwUitE2>8xxO{%2Qk#&01&*UOMdzX)9%qY&R)7 zj*@4J)G1}N-pR0u4eFSCxUpIo+v^4B)L1hY=0=+EyJMN~@_@GLdCW zpIx?ymJ8AcQJ87Hw3t-Cx%>R?soq&%*H!xutFKkx$j+|Xm#=G>e3Z1i@vn??xTgM6 z`^ENKHQh5c-B;qdn$?red`;+5%f*(d{ohzV>3pwe-G!AGnx<-|woa{@4s=etAG)y> z0VldiRzfo&k`etTfTa{ewWYGMcLpWPAJc}z2wxPvQm#s*gQ(je($bjYy7-Pk{Bh@) z^NY4I=W!w9F-yxJJtHbe`yI(-2&kD-|22o&fov+#hcAxZ1oa@uHua~W7cli7n=y-; ze@_heI+%6R1NK4;>Cdi7pSQ&dtQnD*G~;%u-~Te{aQsmL1Pme>Lh z=L|RY2SXb6)!D`JZKQkwWwaCm$ak&a55BqkE4%Y`jhA*`+Hw1ZRVH(p#t&{0-AkOzPUF{q47_+oqRq zyXm}f?&cSMR`)ZJV`HU4s9n=yrZ893l;$_FU ztFqWv674aUNl8&4dSki;RZ1A{LUU@7npi^yD1s(o-3coREDq*uTPj29M5&DKElQkB zN-XWt7tg%9E6S6yjf*6fC%leYwF3wkaT;1U`mfgI>eo$r?=-Xl%lYQ^Jb?O^yFO=! zchU(0c&gs4I$w3m-8SQHn|fx}-IWhCPrI8L&M_L+RpL|D-{Oo+qD2h!D$~ILJSK=s z5<(W42uWl1>$ai*IY5F60*bs2h}(j^$Ay2epA~!U{V*gQJ$^!^ISJ~hhL=hV0A0U0 z`;~QAWgJWF!WiA)bZI!PWwN1iJW}kpkyWF0^pCXvCHM!B-Hw)b?ZfJIEY`0xFpv7Ot{1r&L#^MTteSi0+;sk zyd_-P4HR9{91tSbm-O@=w@GhvR;5cbnbCRj%Jvca(`4vj^~P8=1ANh8`yjy=kpN_j&yLl$};XCOXgv43@V^V&$f*i%pLS9KoN^ zIE;od>@*ZL8qKzpCn-Z{L7IvD^GLFwhoL;Qw9$l-*hOVdQc&E1 zGJ6%*C}^SMOWs+6UZLQxD43w&RSLdBL7BmY#-dX282+?>##%!Lm+f}~-8rc{U)_A3=Vhwx>`vB1^7ivR}BOs^5c<)pN3l6+d6FT7` z$V(0T2;wynR82hy5VTcTyWy^<&M1jDN}|scgObSQBWM$H0jGB{D&7R%Mn;0n5mOT= zf&qKy){2hMoD)I8>2!qd)UGW!@WxncR~B3Zp`B#~4?$?Bdfo?!>2r(`;$Vd0W9N`K z_Y(l+b~M<=B*g(egESO8IJB_C5rCaLu{5_O&4VKj7e9$wl!hY@q`67%SenmL&JSCh zi`(MlfE<*4uQ_1zs*(M$JXM!lq|9%*R<1&NV6?ioj?Hn|ZAX+O?AKxC9JDx`hlF7< z8iDo6+)-)a*pM8Kn@3-!MX67H7M)Vrj7wUd%4S^B-m~@06je5h!+gZgU9@aNX&$zm z=D}Kp9T^=3YH34U`E_?RBxu4^kH0zKFWqXRw`T7)I z>emT2EFjMV)?Ee(xc0wLhYhOAY1kN{M;Mv1Eu|rsr+tLlv`z#Vczye&=*8%j3bid$o!@AODQ(-XpN{`{{N0BC+WueKFRZ+@?&7)&%dgn4 z9Qwf%-+SW9!!v=+)1QA~QhKkmXEO8VE9YOC+B_Rre)Ytpln>O?_pbsSQ*nINHc-)9 zwVgAyomURzYI`SLd0*}1!?5S%TROhm_f{WmXkoUoSZ-R$a?|QPADDDraLr@UTz=*3 z%|o++{nPIKjQ{DJ_yP17TXHrXF|Csw32jBJMgMeiYjl`m8J>KWNz{^WX^r$=tKo%G z$uog#s{fX1C+d_`v1~)x@q?kULHAxo8B6DdYv_~dWF&`tMqLX?muO+&`uQ7_!c;bz zgVc)@e3JqqX}KzPg5oInpZL?J5RfXi_jjw--Be~*9hi7znv}8w?{)Rw?3(R*c;d)3 zsb>$r*S-4Ymf7w-6Njg(R^+5T@Aa&`d1AI_AEm=tVc$Fs%RVR0Ic<)$1$U$4fT=q% zkZU~*geI}N;A0@Pm9>Q`26DxRfzW)y^ECu=Wr4aH$6xWmTt<&LN0=%p3*0qDwl6q< za^OhZ8IeY9aMuZ#PCAE-BbiP6W65OfR8k4UdxLrZumhx1BZ(wA-h|EkI6H#$3}G$p z>&MYf5Kbw~$tOB8Ox{IWaa(DuiF~#ta$OQe#a>&sZNI7@W2r)NpP!KUVC7`*0GNqW^V z1yrrtNx@+XXikGwxDwC(9i^A4@!z933L2;(N_-9OD?6bSZry&@<8t^(`>QXw2yzRp zn+q%s+F$Lw4{@BJR6dTs;$xsqoNGc<89^o41`SIZwZnC%&gvs&5^?LN5Ybx3LdYV` z>_#{67MX3#pjp_U*@E&F=w|HA9dIR5;5{be1aL@$B^uQyrk9^F_Zf!s4m814 zmW4`v_m<(d7`DU8TvAdQ>iRgndSW&D5ZCL87$7*E656S2jIFu_PR1_0L*#FFY?N!icPip3st zQzzSGI~dA=qbi}~Kag>o`7CiXrLbpAFco9-O$u`M0dV2V3GjicjA(4lB?UEXE~Sh& z;d9B{X~Q@!Cf87{FWYJ!Kr82Bx`$jcuzIJ4b-%H+aucfeEY7!Z)HnjiL~ndBrg9h>BiW91`%wCfCZfsN1(*|gR{Z#+g)$JeDz#z#e+HDL(>vJiiQ^~b5kwOi>Oi{S~1#o0U-ZC z8JnvGst^1C;Sp$mt$G~iUZ8;^yJ;V3HYF{DEm<0$G-VtJs44i|K`Duc4d)f;JL>mQ zf03(bz9dT5(PDW60CW4pIQesaf&T%vdA3TX1gle^#jJ4u9A+~)%Y0}2W)IE~waOkC zD}X6___3*-sYANkBo;Pdj9+j-N;rz< z>O#af7ySwIPHKXjox_<^5BCiPWd4OSB51OPG_O?YaCg6k-l@;0>)0x*o+~ zHuKj0*+Wp9;15k4Ws_Bre^3EC@&P@>IU*JK)+URuN#pQ<0d?%zHT!Y+pHWBRnXpww zDrs(Prr@4j#cEL3hZ5MUKs8{uA;KIzBPzSy!Dx%E)iCBFO#UI7 z{9#uy^7b)TWvJN3GNE0w>#kG$It8PG9>4{TB+!l(kvaK8Nb4~gHH1KSKYMWRGmq@W znJJO5Y_Zm>jYbT5sd?lXCgiOI05Nn6{^r}kWw(MoGr^wQHBHma>vJ_5@=cwPFuxNL z=5Q`%^?yyAZZQ|sr7?*M9$^EUhVNrGXrNTxq;hnsIYP!Pf(6L3qijVy)uJSP#~JZV zT>S>6U#Wzv6*IvV3|DJ%HETZwR}v~={6{OMmH$3jRguN4uVjU$2|nBXI4+Qci+5fa zgsU)N4eC*10Nh9X(U-Y%*D(dC_KwG3xgLyl%6y z>N|*`=PNeZHEDzPCBEi|_Epinqoi$)pc7Pv;U>j2pQC5rca$Oo`5&d0$+KVHBW?s))nWeT6Il60Q8Raz;Q7+BG{xa7f@ zDQ+f8id_lf_l{*Di|x%4br#PxiV7m>^z13s^NBSS6n5#TbT z^QE&f^|Yot`57UuN`a<^iel)a0bHG-dt0VnlI^60;T&dmP``L5s4Ua>NpiAi14`iS zns6RIX+sx>$jkEN#gq9^%cVyzJ~}m;3-#U(HC+x}j=#NmYVfUPSG#7K`))XY>iv=T z=0mv+2XmoAP*^W_UXk8feP!SWXTEplMoVtx&RqR2oCVBRxoXKFFh~x8{`3A@?$#N1 z>(rrHcPGvbCi!(6-Df!t)qRgY{zMcrUi7hppF4!@nTxKfz_z4UAARy*|K3N;G^}rb zBqVL7;4lS*R>YElnRe2+oQ5lh4u%Mw`A7}D3~c&R>7kdDQReBY{tqhmPY4Wj?&0da zw}d@)9;ufJQ$#S*;GsIye|5*ifj?v8-%w~09Kl;sRH@2+@hIyj8D9ne~M3WJ|Z^^>uh9IdMgQ}xyv6DOUJo5 z^{hDmjM*z4#f{lb@xY7(|8Xq79mj!2WncuqBS4lWGMJWDVo*uz!x8cG=qb1oYbVSp zZTL{b6p$v(&n+keC_|`Zt1DB-*a3~XuQF8M8p~45&%4!+Q0OOw|D6DU{-6-(yj>Hz z@WQS7wKMf=b2aPoRju<5d%!ty7~2&GaKC&;T7JcKE4*PQyx~UAth5~`P!s+CzoWW! zF{()cU`ST~nK0;3+X;hV3MwO)mV5O#6nq0g8FGD0D=^S{59$Ar(7Fo%M{7IKI$hs$ ztA4{w{f1o4#(Y)%{o|GPoaQ3|U`y(A0IV!bF1v8jF!JJ3L&c>OW(KOE?7>dYC|V;>OPzfZQs0M!-h>9pLUfrJPV83Ig9>*Vi^KSFkz|WvqDW# zLopBe)qg?yEMajY0AR5i%$cw0{Y)HL5pxGn-|M)8NtaS5MW%YV^fYNj}L~~99u$9CQbLKU@?Z#&SFDes45uq67IvHB~lfvH^+;&R=tL03U^g; z{n#%%;A`UmR3$qBIq|b2bT!!pKODN6>|SKz4$2V}cp?4kLFu4ZuiMcdc#B1YnInB-5uT7)0@5mX5Q?20r5JxB)@$Be5r z^s5exi*$b~HjFzJ{A5J^E^>0ON*XGgx>26^jJJT6Q;7`srQJfcdRbSZ>_;c0L`F%m zHZ>v-KQb|ABRks3KJeKY5yc7^g+{)l%$xakL(6RInyV*%c=FoGTk8(btULU!{oSW? z>yGCdPD~!Y-Q0dzp6%Q`vuw+a(?5ObM=$-X=iN=Yt^K*?qqv{-boAGu7vDYc&FJ;e z_4th~KYj2=58gcZvrW0phjP7#e_elMI`rb?K?E>hUfuS?9oKf;82wpSZq4C$M`r7u zp7uZe@dtLHJ|=#wkwN}TdmHwx6aPcg#(k@#pL@ChFOUh+`I$_79o_$bA`?YP0EZQK z9O#Ad$Re&>8hOn3xWmHfrSgu7V2Y2aFUhz`t4o8KRFYKqvqu1M@o}j5 z-77&2P!Obmgo+hpW(@3~NW4@h#nQa22Q;kS!%9i0UZHf7rv;+qf^wRQWweqosZ~_G zdj}QE~MFr7<7Z){{eeknO7cWT;(c0gkgB1kq(V2CRzPow)$!Bxxp360S z4iZ+^INP-9YR?bXTw8Nv;O42^>b<$TeUk@nFYlb~+Hynw>EMqBZ)UmBEk8Q#Z@b;P zdZu;v^txRLCm)As;r`Wko8D@=vO3qij<#>lU3_k8AXnd=Z)*Fl`z`mCW*j%r?r!kn z;M7^%iEUVuZ|;WwZm1e}h^neTW(Trg+Hc?QSbU`dYLGZb%h7$_=5X-K9NRuS>R3TL zWX5vaqpp$PVOX@Pvu+We@NRwjO%LYFp56J`yH&ab;-tP2+wn^vb&ZpceXPEQ??o3t zJ`HJqjY6hg!IewqWuM*gmvxWb|L`p69gdtsO~(BXr-}v7*eI86nUA0&{HHpscN*~f zbwbvQV{-G}!3&gaGX-p;ZHV3qiU?DHAFUIzLFU)|nQcpo61+P-VR}f&yX4)De1w&P9p$z{U-~R+a>jyx;vQfJ- z=U(||N%Zv1J0;IE;=JGIsh(>QJxy?a_cX!z-P3fZVFR4s=|gD1RRn_ELj4LjzXS3L z)o_0T#Pl|mTFGvLfda?U(c#3ff(2aZRy6v18WkZ1n&gMo-HxU?0YRZ(ELtS;?wxse3-)jhC;IPSHZ8UD zO)v@-AFvJItdTS=I=_3^JeDY)pqW4Eq=hrFL^4gk_=LU0QcgN?vmJDnd8^maeDWe| zsv3Xjy~~7=R9F8;3!5%$9&_H%zYf(_%s_>UQE;Ahuuk z6no8<0{jk_Lydt__)#(ZDpdv=KRX_DB!*-KH{1ES8?G~BCBs2sMjb5^(qEv5*)_#V zqsD?_THP#`Hsg4>jf8SLO2NXmcH@oS+}fSLuGw|vfp2zCIli@ax@Om;6G6Uv!;O{G zT@S(cJJ>MQGUbQM=Cr%};}0A{?QYhEuh*`T)SsXpZt0%}Wc5~PUb0JS7ZBS-y&&?^ zsiEDm5-eOiG=yf$WSSH(;N$1i2J?8(W4cgjkyUBK#J3lI zxVs>KA9Da_-sS3DD(d7Y*4@LSa%>3R9qe3BZ!MpJaNk?$<1QiG^?^|p;>XWVIUeZ< zTzByfXlf%R(%H3MMRvH@a00)m7RSbjnMoK4h&tQ~b&{Yk1%yYYuF=mt(QzAoX$`+M zrKOWE!xJ9;CsFz>Wm-Nao%{==IK#4gxHQG8!m@!y5^_l3^i z3afrAtax8&dSB@Ht*~}RSPS6yg8w%{)%!x*hmuRQeJD^66vPKUlpLaMPCx)lcc^K~ zKK0C$Z>FwmS_u9wP>*nyr~fxekQ->tv{Gl2&t{PRwi*nEM0I7w{2 z8xm~33!z!D;X_-4cuXw7EBBZ-e{@qsNQ2C4jt0zrkoQh7=)*!*H$ z9@h}?h?cl GjQ;}%fSkYp literal 0 HcmV?d00001 diff --git a/tests/__pycache__/openapi_mocks.cpython-312.pyc b/tests/__pycache__/openapi_mocks.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b439d5880224614b8bcc6cae27030ff53e270230 GIT binary patch literal 41440 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 YQr2>xW3l^3tY>smo}Y6p##;0L0k<`28vp 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 0000000000000000000000000000000000000000..f7bf11bd116be691b94a4828ed6992f22f680e35 GIT binary patch literal 157 zcmX@j%ge<81V1L6$pF!hK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%SJyVKQ~psEU`E_ zH8C$QGgZH!C_gJTxujUXC^20(H!&|UJ+(-`B()eQQks`pq8}fh38doV^$IF~aoFVM crrvLx| literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..014e303f0220c33007891dd030bb16cfd2bdcced GIT binary patch literal 194 zcmX@j%ge<81hdwi$&dunk3k$5V1zP0a{w9B8B!Rc7%CYxnW}`GeLNLPQj1H96$%oQ zvlG)(_53s$Z?VV6r{pKc$FF4g4AS_^K|douH&wqZu{b$3F)uGORllGpKPxr4q*%Ww zF2KczG$)vkyQXduWr#UQ7A QU}j`wyu~0;LzyJUM literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4fc9d8ad1b7e5b4073b7493b23c97c943ddaaf94 GIT binary patch literal 10856 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 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 0000000000000000000000000000000000000000..cf9577d12e52c106a4b9c3a39c9cbbfe8fe617ab GIT binary patch literal 197 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d386fd940fc62706bdb90391cd9e11d399ddba58 GIT binary patch literal 7574 zcmd^ENo*X)747b+S$k%0{@qs6w`<}O)VBuk7eS(YO@h!F!J0yLV_O>)TDQr#n& zlOBKw1eo$6_9ZY6I`EK#2{M3=4sy*cIm~bY2Mr5IU9ub7kQ1Cv9GvZDjn2!Wq(8a8f6DLFwc_}MR z1XxOP!L%d_xzI$Il?1YpTy!GJc#@6f5_R!@M3+|G`Bp?*nDw`H)mU!1 zAm{-}u*Fq3e--rNCkmF!3BoM&viH-89@Im6SdXklSA)E?B0Wgzv0e}=0Xr}o+S>;5 zi5{s0ZDI%KPJarbzImNNfl(g!O#MdUiyFN;GVAY-R0rkis z>jk}fdg5WA4D+gl^`zcnhchCRB36qx3SW<{US2b`Bc3KN{f(##?~0WG{1NyIRf1r{ zRekXn;7{UZ>=m|*=EDG!@icbXK z0whl_ScYk-X=-Sep?bHPuIffHTbR!oc`PgB2QOy}X)SBMI`~>Cmz%$tE!^96@!>^t z3@^URZhsG?{Vq4S&fE*Ye~sy41}?hai@z1$7FMM36(CNiRMcTa6cw)o9AX;TDJL{{ zPovYO6G>}Dt3(am#CfJt3r}wi%@lIR&|S?;8*r6pjG-be%o=IS9HQFvKu*hR(*_;F zxtc?ze8w7@f+?6RqcLkbSe$nfR>3#<^388+p21yu@EY2A7RXl%LOtI3!CMdBTDrLz zKly3LiCRbh@;gAP@%|mCAdIgN+6Ji;Doo-GoDel&vUzR_;zU$651vR>otUcT3h0D@ zkE`mhN?Mke&`xN-B2ql4CK`N!cA&;+WAP~N@Fe{0fIAkxx-a}&lA;3?PaEw6;&iz? zB9os6n}J(Y(O~5yZiazIX1Qa=vWn)Vp&{d44SnQbx{z}M_l!x0V9j!LIQ0_$rvx2> zqjO?rp+wV$TB2D;c6n>QXgCRP_o+;5K?SPuZjlIDHP0R&^DuTZisY4DV?NKvd|pX7VbcKgVo{2|-8uFR$C;kQ8nJ7|dA74P z>a?kFva>2RiWF84rf)b=)zqd8%yE)q%t){RHz+Mor$e18(41!B%4FeK;ofiv&cca< zQgyPF$y%Ab>4f-=;q<8HOrez3SqJKLwlJw>*$!4(9649W&l&SY%}UR(eMisX=wwvH z7iZWnjQWLF{K8o-@S&ae3m5&>&iG4qDH!pWobwA8_%Xs0oZ9tW
_`L){ScXV6}T zKXU;NB;EoiPA%N}4+#VZb_5_#VnVQWn;fZ;BM+n1lb8PZ#-HBYB)2|qJG$L=uGV(0 zo_Mz2ak}2wTkn3R-gELxIn*9nxc#J6heQpG}#jB9k`PqQXtJAFndh#P5m3{p-(a8$yXbZ~FuV`JnTR`oM&zDg$UVW3dxE*k?-jYj3nex! zuF0$To`4VVdqR&NfZwB)U=xqn!L-^%}TdZ?(|Jv2Q;->BD%vzP{0~?fEQpVC?gt1@*VsXB1y=0hq7V z*ULU_?8ZyufO<{L*Y+^T-ynLs-eE^v%onwec;oeH+7G)n5WP1qKMn`n7lyxJB?LR8 z(|wP?o~gv(brJKuNSf=h|61dVqzl`8=W8AF#;td|FA`ZlqW9RcYl--wEfM!Ld8ywL zFk@*JQSP2k?{%#b{vp}~UIa(K?Tg^@fetN`2Y7T>%YJXK0*c$1CXk>X_FzAAzzpn` zZ~s4o-i_4}^-+9*GA}Vs@xW5tVe~m5K0M74%g-V6c`QvKX&`8N9K>?FXDT0N?(r}) z;H$!*A2`P_$IMs-I`6~^MI)~Qk}hSOh^OTXoV^oDmrSdWQ^NEVcF8)&K|3vw4bL4z zoz&8EMqZ~cVs5L~TFqthC9o6yF*Z|xKqM>dWxmAZ@;l#vsXawayn4|CwZOQb79i|t zSLYSf1t?XCRVW6ClpsURbO8Imh~yFw1hyB;Ej}a5o zfqG}E-gBznovQa9t0%h`C%$ZnFhtqeM3iF;QJ${Jr>kV_lSv?JZ+tSj**^wc6*BwB zYVz1N!QYmb!^I60`#8VOia6h(Ojo1s+8dnR#BMl?t@>T%jaHmNAbPVVAFC2&IkHrS zq$(dMI3f-1dH91v1J_?ArO0GY$fmG!)kL;R! zeVgEK%gx}@29r3yj`ca~r<>&Z*O6u;M*KcRdf$sMq#4l@ixKd|f&1}8#DxijQ2?vr z2Vs;T=O8W_Tx+0@isXwD2v|UJ@k0EEIDfzk$#F++MjlKjn9bxe zR@&P*aM!S?B6vfw5mmIs2a+!0&1(z5Nyve!z&M=-j?N%K+^GaOkGbH!Al%T5-SJm+ zY)YGh_#?myqkM_og^c%w;!HJ@zgw6yOcf$RQxF(yqK{@zb=6&Wc1LGgu`!Mf;F39IXWwSYh=G9A61bYw z;?<=|=wX}SZ;Q)Z*g%1^>sXnye!59gY^4>#EL(2p!LGqOEZX!_7Hz`CqeUP}I|fpx zN*GX9Rf-p@6Y-*03^zEvs;hvzeu#JtQsW-YSP&^|WI16sk1ezl!SFTyFCv2@P7q_7 zCX3-ZA+F8o;&D{9m?;|BOx_q|F=>uwFkHkuJzFz&h&Z;yKo}1yc6maM|TFh#n;3yM-$?4rs}^KeRVGY literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cdae77c4d23ed4dd5f1dfe934b8423e7648b473e GIT binary patch literal 167 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 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 0000000000000000000000000000000000000000..80dba8b360c84540e28eff7086d449aab5cb4db6 GIT binary patch literal 165 zcmX@j%ge<81Pdpf$pF!hK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%SAsUKQ~psEU`E_ zH8C$QGgZH!C_gJTxujUXC^20(H!&|UJ+(-`B()eQQks`pqF-E+50pvOkB`p;a^vIm j3Mzkb*yQG?l;)(`6|n*hWd!145aS~=BO_xGGmr%Uzuzi; literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f7e88c5af23c6822fcf0130b896959c075032a0e GIT binary patch literal 6829 zcmb_hZ)_aJ6`$GLz1#civwe1)IB72-e|$J`91`LNLJ~s4pF#~JK-FvLYID1`*V+5i z**zQ|XVruV90^3I)CQ@klvY%Yn^a2GFMQ=2Qv1QirZ%f0MM5g&o586H?w7tdyL)$b zE)Z3n_}Kq7jXnBu}%H zVqGwWY=A$-yp$6t$p^C`-V-q8d^j7<%UL-e$woZsXf_Jcq8ZD_v+;Z)o8WxOY{@6H zNlFCr7}0{~i5Ak6*8=X!*;YL|y46>w{v6$2uluz-NM`*0`csny0@C-^IercF3tx|1 zgWY1eqf}Ij)uo07Uta6b;^)O{?qK3Svz>R)tjQi=hD3da?7F)jc?Zd6yKlMUzLH@5 z*80xwDAxvZ?RB|T7D(0guUVjLT*!2Oj&{jVC#n_-MO(FvV!;~B1f0P0&pqPEnriE| zk=LCtQcX8)H6u9kv0|>A*9*22KWP`4I;=lZEDRaLIL%Sj(miI*GdX{VZi6oW7Amj9 zvDHbIY9uRY6ppI^{{okVFlqd!MW@IR)x`6Otf<8_>AaYgKrVQh)}^$#7CIly25;jh zG#U7z+xRV782IvS{G=8Eet3$UrP;_yl4*6KC++fSZ!48Ou)`eFZ9Uzv!Sqm(rS-Eq z%NdqY7*1OwD$}*JSsXTUeS=`AAk(d~X*-h6Sdrzt0}Lma5OgK@(d8ph`31d1YiM2^lBTbTA`2l2tD0%#jtvC>XY)I3dMbKd5j8 zGLj=&x;f+obNqd=7);ONK%B6m`{5T4CZ!s==y#zNQK2vC=@k znk2UpB%HVqnw7iW>719-i|aN`iEqRfJG-aEH>1lSLvo%~u+DAyD%uhE@+6^fvc}0D z17no60Am`CHcLUp!ZB&K4yY4B^BAgWRP;(`qdIl|dK`i{`%hG9v8Da%Wjw&=1d+la zZ^;!+6U+dA;UVE{)2)?AX3Q<%jBMdr(ZGi4ffa?VA<@piY1Y8SWDA?UqQbNP& zmDWc6IBO${LP&5`TZ)ln+t&=4&9y+&x@H+BC+mh;rAOG;!)l^Pl&=ae)6R`l*4?I# z+kwHr!h^)^#g&>0e{FG_1ti*BwT3t58|YE4*R*G!sIQk|XiYQ=0v>@!R@xesa(O#Z zpH)Wc_$DKrs2QnBMxnm8yb^29KSzOI^UC_D2192A)(#!44GG4U)vs9wOksxplD^t8 zeC|FRfTMi7#*%M6c;UgB=$6H{&P%;-_s+yN!RdUfdZ9WacY6lkwu5G5)`1JkNYKtJ zEk0(atLedFLFaB?NgA*Vd!SxPn1*Hl*kAZXK7#GWu_#&z^KOuT5eDo)c0UrFw=U&~r;A1N8w6M{ z$Y-Jmd+3V*dl5y+7qzm9^_Zgkw5*z5Pe@VVNGS@3BG!!yZAOB}m-QgQ?8AwBV3^ZQ zQ&9jxI)Td(?tXO{o^3{3rvG^by{CjpRZol`7Xjs~KGr4I=g6EPk{^XU?lOcIMkLqsgznukR82Wgo9Gs&9)6o5C)QRxmuW0JHzRsj(a!Tf?}gyY~pz(HIDvTMNI%xP>y3ts%NXX6~aRE6ucU|507 zcp@N>D&)FV;aB9>kUCTY2E8u&8Q+9xBX+wZX;r)~)q13=G>R{6P0@l{=(_Any$bo} z%dnSU3YP+}NfMcW-iZ)|;LsU>?+LjYtjc9F5w3=<6&q2Hmr;b;HARb5Wk_M7ICe<5 z=Iv06*%%9X8AV`NQ?7aKmx$HTFh9nzHKi)}wqlKvcwX0(x@3XE`gN_F$%gz{wfJSB z5`;ZR8b&nRXQCR0eYT)w8upp|zwI;VYxJ%AtVzN?lhugU>g^z^wQ23us6qLDVm5|& zUK)0W!Ff%o#{8B2=Q*l{&(aC}$rGXR@Bq0;-)QF%K2lhfVcae^_vV8jr3^vpW@GA| z%F&-$Uf-;BUXWatAbBJ3>(z^aq?Csd)lXJ<=$kf%&b6W|M~|pZK`tyH~ef ztz0!{dLI1X<(X6Z%;3;`%kbpme^0D`r|(ABeBy!0$8M&&7g9a5sh+DFKM-b8J#(oi z7ZOhbf7^~3vFBpt%8A)^SeqIEG864t5b^ILx65tR=qz&G)?B?hFZNtHQImm=sexL{ zt$bMLHS2bTmL$^h6#b8d$=A7u*B{?Q-|wc!`^5J*9&3ljs&9x|sCoth)Y?!5VTN9> zT_#M>DjjVMb1*#?E#Sig1P{6@cnF#}5r6;?fRG_pMa#6&33wUB^S`EOQdI;f4dRG8 zls1gPoNi62gZsD0)wI+?9xO_ltc9zR2a7?w791O9!US?nsn%fD-#5S_1xOU&2m2kT z1LTdLaZrp@`aDp46ueY-0ntxz)8mG2YF4^XtYKu|AbS{Ab3%E;!q4hQ+2hch_MmEy zi#`ZG`!d!vN2xuCF8fv*A!ok}Is2zWZnE|?Ou?Q(@+^|)k-UK9hd?qR_9Lukfiya3 zKRdOCb6=yE-rv|5u<#Ul4(hsR@9pmCCe{{!3RnV>NAWhviZB}WvltN9Q)SY!0UR$i zpV$u$*4nkux^1>~+ZAK3wSOVnKO-KvaU94@v>%?)0}CSledICR)*NrS^$$_rtv1qm zF0x`JVcBzAj&7&#(xY3&-yJ1Tb>jTmz=yybh|~)D0Q~4OrigHRQA_Q#TA~KGDDcS} zEf?TkqEuwIu0bejA1vBW=h9nI*(F~bHA0KLn6Xb{!^bjX>*N9U~O8prD zaRPug_>(5Y@jwIO+ zk1@lB>*E^B33)shjWHC~Q$Z)_&fs(d?qm8_GD^B>6!g9vT!!_*SG{gp?(GBG*V(vM z%uB-h@amD_Hy}rUN6f42j1y9=@j}ihI#Ea>;i>>GUMjjH@dnS7I0*BXa62NoHwuT? zYcMHdokf8z1t_JTkS(7Pa-J-ZU9)7@rzHLvIs6IPcPqGuZoJrgi@;-P1pXsMe!csZ z?gg5frKv?4avNQTzu9?}-58$Va%e_8 zyetI-;Y&G8Gs|J9EeD7IU-Qs}5BbKKzZ4|;fZL0;rR1vKHS4zDlv7i|w_+D!vmJ+L rjy*pwzpz;647~UBy!_OX6p(_;QV>>8Nc5rQ7O3(0zeqxp!|;CrpvVAS literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..52b6cd563d176d61c7af92e46589aa7b99a8abbc GIT binary patch literal 18917 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8c7cdf2aa94eaee8753f18d17b50b5942d89a6b3 GIT binary patch literal 15886 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 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 0000000000000000000000000000000000000000..0316e934862143b5e17251583a203039cf53d68b GIT binary patch literal 194 zcmX@j%ge<81asD%$&dunk3k$5V1zP0a{w9B8B!Rc7%CYxnW}_BLOm5qQj1H96$%oQ zvlG)(_53s$Z?VV6r{pKc$FF4g4AS_^K|douH&wqZu{b$3F)uGORllGpKPxr4q*%Ww zF2KczG$)vkyQXduWr#UQ7A QU}j`wyu~0Pjya-0co(l_a&X+lCu`X~LA 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 luDQX%WAQ28%e!xMzp)Vc;+{cO_FMMP_yt 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 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 0000000000000000000000000000000000000000..481ffebf5f5d2f0611b5d51df976020c9d39ba1d GIT binary patch literal 163 zcmX@j%ge<81V1L6$pF!hK?FMZ%mNgd&QQsq$>_I|p@<2{`wUX^%Sk^YKQ~psEU`E_ zH8C$QGgZH!C_gJTxujUXC^20(H!&|UJ+(-`B()eQQks`pqF-8)nNzGEAD;>2#K-Fu iRQ}?y$<0qG%}KQ@Vg(w>2*kx8#z$sGM#ds$APWGHWhxB- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f5e51d2fee9137fdc930da5382ec9f251306cc9d GIT binary patch literal 9001 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 O>1Vkgqekc89sdusPtPI% literal 0 HcmV?d00001 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 a0d5685aaa522adb9480f1d68eee60a756464d0c..903cd048d7aa7fcf065218cdba4ee628bb3e80b4 100644 GIT binary patch delta 22 ccmX@ic9@O#G%qg~0}$NmJCotCk=KkF07^gxVgLXD delta 22 ccmX@ic9@O#G%qg~0}!|{pU$w~$ZN(706>-nKL7v# 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)