Full Code of arc53/DocsGPT for AI

main ce5cd5561a65 cached
642 files
4.1 MB
1.1M tokens
3205 symbols
1 requests
Download .txt
Showing preview only (4,410K chars total). Download the full file or copy to clipboard to get everything.
Repository: arc53/DocsGPT
Branch: main
Commit: ce5cd5561a65
Files: 642
Total size: 4.1 MB

Directory structure:
gitextract_kso0ck8g/

├── .devcontainer/
│   ├── Dockerfile
│   ├── devc-welcome.md
│   ├── devcontainer.json
│   ├── docker-compose-dev.yaml
│   ├── docker-compose.override.yaml
│   └── post-create-command.sh
├── .env-template
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── holopin.yml
│   ├── labeler.yml
│   ├── styles/
│   │   ├── DocsGPT/
│   │   │   └── Spelling.yml
│   │   └── config/
│   │       └── vocabularies/
│   │           └── DocsGPT/
│   │               └── accept.txt
│   └── workflows/
│       ├── bandit.yaml
│       ├── ci.yml
│       ├── cife.yml
│       ├── docker-develop-build.yml
│       ├── docker-develop-fe-build.yml
│       ├── labeler.yml
│       ├── lint.yml
│       ├── pytest.yml
│       ├── sync_fork.yaml
│       └── vale.yml
├── .gitignore
├── .ruff.toml
├── .vale.ini
├── .vscode/
│   └── launch.json
├── AGENTS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── HACKTOBERFEST.md
├── LICENSE
├── README.md
├── SECURITY.md
├── application/
│   ├── Dockerfile
│   ├── __init__.py
│   ├── agents/
│   │   ├── __init__.py
│   │   ├── agent_creator.py
│   │   ├── base.py
│   │   ├── classic_agent.py
│   │   ├── react_agent.py
│   │   ├── tools/
│   │   │   ├── api_body_serializer.py
│   │   │   ├── api_tool.py
│   │   │   ├── base.py
│   │   │   ├── brave.py
│   │   │   ├── cryptoprice.py
│   │   │   ├── duckduckgo.py
│   │   │   ├── mcp_tool.py
│   │   │   ├── memory.py
│   │   │   ├── notes.py
│   │   │   ├── ntfy.py
│   │   │   ├── postgres.py
│   │   │   ├── read_webpage.py
│   │   │   ├── spec_parser.py
│   │   │   ├── telegram.py
│   │   │   ├── todo_list.py
│   │   │   ├── tool_action_parser.py
│   │   │   └── tool_manager.py
│   │   ├── workflow_agent.py
│   │   └── workflows/
│   │       ├── cel_evaluator.py
│   │       ├── node_agent.py
│   │       ├── schemas.py
│   │       └── workflow_engine.py
│   ├── api/
│   │   ├── __init__.py
│   │   ├── answer/
│   │   │   ├── __init__.py
│   │   │   ├── routes/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── answer.py
│   │   │   │   ├── base.py
│   │   │   │   ├── search.py
│   │   │   │   └── stream.py
│   │   │   └── services/
│   │   │       ├── __init__.py
│   │   │       ├── compression/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── message_builder.py
│   │   │       │   ├── orchestrator.py
│   │   │       │   ├── prompt_builder.py
│   │   │       │   ├── service.py
│   │   │       │   ├── threshold_checker.py
│   │   │       │   ├── token_counter.py
│   │   │       │   └── types.py
│   │   │       ├── conversation_service.py
│   │   │       ├── prompt_renderer.py
│   │   │       └── stream_processor.py
│   │   ├── connector/
│   │   │   └── routes.py
│   │   ├── internal/
│   │   │   ├── __init__.py
│   │   │   └── routes.py
│   │   └── user/
│   │       ├── __init__.py
│   │       ├── agents/
│   │       │   ├── __init__.py
│   │       │   ├── folders.py
│   │       │   ├── routes.py
│   │       │   ├── sharing.py
│   │       │   └── webhooks.py
│   │       ├── analytics/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── attachments/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── base.py
│   │       ├── conversations/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── models/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── prompts/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── routes.py
│   │       ├── sharing/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── sources/
│   │       │   ├── __init__.py
│   │       │   ├── chunks.py
│   │       │   ├── routes.py
│   │       │   └── upload.py
│   │       ├── tasks.py
│   │       ├── tools/
│   │       │   ├── __init__.py
│   │       │   ├── mcp.py
│   │       │   └── routes.py
│   │       ├── utils.py
│   │       └── workflows/
│   │           ├── __init__.py
│   │           └── routes.py
│   ├── app.py
│   ├── auth.py
│   ├── cache.py
│   ├── celery_init.py
│   ├── celeryconfig.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── json_schema_utils.py
│   │   ├── logging_config.py
│   │   ├── model_configs.py
│   │   ├── model_settings.py
│   │   ├── model_utils.py
│   │   ├── mongo_db.py
│   │   ├── settings.py
│   │   └── url_validation.py
│   ├── error.py
│   ├── index.faiss
│   ├── index.pkl
│   ├── llm/
│   │   ├── __init__.py
│   │   ├── anthropic.py
│   │   ├── base.py
│   │   ├── docsgpt_provider.py
│   │   ├── google_ai.py
│   │   ├── groq.py
│   │   ├── handlers/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── google.py
│   │   │   ├── handler_creator.py
│   │   │   └── openai.py
│   │   ├── llama_cpp.py
│   │   ├── llm_creator.py
│   │   ├── novita.py
│   │   ├── open_router.py
│   │   ├── openai.py
│   │   ├── premai.py
│   │   └── sagemaker.py
│   ├── logging.py
│   ├── parser/
│   │   ├── __init__.py
│   │   ├── chunking.py
│   │   ├── connectors/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── connector_creator.py
│   │   │   ├── google_drive/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── auth.py
│   │   │   │   └── loader.py
│   │   │   └── share_point/
│   │   │       ├── __init__.py
│   │   │       ├── auth.py
│   │   │       └── loader.py
│   │   ├── embedding_pipeline.py
│   │   ├── file/
│   │   │   ├── __init__.py
│   │   │   ├── audio_parser.py
│   │   │   ├── base.py
│   │   │   ├── base_parser.py
│   │   │   ├── bulk.py
│   │   │   ├── constants.py
│   │   │   ├── docling_parser.py
│   │   │   ├── docs_parser.py
│   │   │   ├── epub_parser.py
│   │   │   ├── html_parser.py
│   │   │   ├── image_parser.py
│   │   │   ├── json_parser.py
│   │   │   ├── markdown_parser.py
│   │   │   ├── openapi3_parser.py
│   │   │   ├── pptx_parser.py
│   │   │   ├── rst_parser.py
│   │   │   └── tabular_parser.py
│   │   ├── remote/
│   │   │   ├── base.py
│   │   │   ├── crawler_loader.py
│   │   │   ├── crawler_markdown.py
│   │   │   ├── github_loader.py
│   │   │   ├── reddit_loader.py
│   │   │   ├── remote_creator.py
│   │   │   ├── s3_loader.py
│   │   │   ├── sitemap_loader.py
│   │   │   ├── telegram.py
│   │   │   └── web_loader.py
│   │   └── schema/
│   │       ├── __init__.py
│   │       ├── base.py
│   │       └── schema.py
│   ├── prompts/
│   │   ├── chat_combine_creative.txt
│   │   ├── chat_combine_default.txt
│   │   ├── chat_combine_strict.txt
│   │   ├── chat_reduce_prompt.txt
│   │   ├── compression/
│   │   │   └── v1.0.txt
│   │   ├── react_final_prompt.txt
│   │   └── react_planning_prompt.txt
│   ├── requirements.txt
│   ├── retriever/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── classic_rag.py
│   │   └── retriever_creator.py
│   ├── security/
│   │   ├── __init__.py
│   │   └── encryption.py
│   ├── seed/
│   │   ├── __init__.py
│   │   ├── commands.py
│   │   ├── config/
│   │   │   ├── agents_template.yaml
│   │   │   └── premade_agents.yaml
│   │   └── seeder.py
│   ├── storage/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── local.py
│   │   ├── s3.py
│   │   └── storage_creator.py
│   ├── stt/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── constants.py
│   │   ├── faster_whisper_stt.py
│   │   ├── live_session.py
│   │   ├── openai_stt.py
│   │   ├── stt_creator.py
│   │   └── upload_limits.py
│   ├── templates/
│   │   ├── __init__.py
│   │   ├── namespaces.py
│   │   └── template_engine.py
│   ├── tts/
│   │   ├── base.py
│   │   ├── elevenlabs.py
│   │   ├── google_tts.py
│   │   └── tts_creator.py
│   ├── usage.py
│   ├── utils.py
│   ├── vectorstore/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── document_class.py
│   │   ├── elasticsearch.py
│   │   ├── embeddings_local.py
│   │   ├── faiss.py
│   │   ├── lancedb.py
│   │   ├── milvus.py
│   │   ├── mongodb.py
│   │   ├── pgvector.py
│   │   ├── qdrant.py
│   │   └── vector_creator.py
│   ├── worker.py
│   └── wsgi.py
├── codecov.yml
├── deployment/
│   ├── docker-compose-azure.yaml
│   ├── docker-compose-dev.yaml
│   ├── docker-compose-hub.yaml
│   ├── docker-compose-local.yaml
│   ├── docker-compose.yaml
│   ├── k8s/
│   │   ├── deployments/
│   │   │   ├── docsgpt-deploy.yaml
│   │   │   ├── mongo-deploy.yaml
│   │   │   ├── qdrant-deploy.yaml
│   │   │   └── redis-deploy.yaml
│   │   ├── docsgpt-secrets.yaml
│   │   └── services/
│   │       ├── docsgpt-service.yaml
│   │       ├── mongo-service.yaml
│   │       ├── qdrant-service.yaml
│   │       └── redis-service.yaml
│   └── optional/
│       ├── docker-compose.optional.ollama-cpu.yaml
│       └── docker-compose.optional.ollama-gpu.yaml
├── docs/
│   ├── README.md
│   ├── app/
│   │   ├── [[...mdxPath]]/
│   │   │   └── page.jsx
│   │   └── layout.jsx
│   ├── components/
│   │   ├── DeploymentCards.jsx
│   │   └── ToolCards.jsx
│   ├── content/
│   │   ├── Agents/
│   │   │   ├── _meta.js
│   │   │   ├── api.mdx
│   │   │   ├── basics.mdx
│   │   │   ├── nodes.mdx
│   │   │   └── webhooks.mdx
│   │   ├── Deploying/
│   │   │   ├── Amazon-Lightsail.mdx
│   │   │   ├── Development-Environment.mdx
│   │   │   ├── Docker-Deploying.mdx
│   │   │   ├── DocsGPT-Settings.mdx
│   │   │   ├── Hosting-the-app.mdx
│   │   │   ├── Kubernetes-Deploying.mdx
│   │   │   ├── Railway.mdx
│   │   │   └── _meta.js
│   │   ├── Extensions/
│   │   │   ├── Chatwoot-extension.mdx
│   │   │   ├── Chrome-extension.mdx
│   │   │   ├── _meta.js
│   │   │   ├── api-key-guide.mdx
│   │   │   ├── chat-widget.mdx
│   │   │   └── search-widget.mdx
│   │   ├── Guides/
│   │   │   ├── Architecture.mdx
│   │   │   ├── Customising-prompts.mdx
│   │   │   ├── How-to-train-on-other-documentation.mdx
│   │   │   ├── How-to-use-different-LLM.mdx
│   │   │   ├── Integrations/
│   │   │   │   ├── _meta.js
│   │   │   │   └── google-drive-connector.mdx
│   │   │   ├── My-AI-answers-questions-using-external-knowledge.mdx
│   │   │   ├── _meta.js
│   │   │   ├── compression.md
│   │   │   └── ocr.mdx
│   │   ├── Models/
│   │   │   ├── _meta.js
│   │   │   ├── cloud-providers.mdx
│   │   │   ├── embeddings.md
│   │   │   └── local-inference.mdx
│   │   ├── Tools/
│   │   │   ├── _meta.js
│   │   │   ├── api-tool.mdx
│   │   │   ├── basics.mdx
│   │   │   └── creating-a-tool.mdx
│   │   ├── _meta.js
│   │   ├── changelog.mdx
│   │   ├── index.mdx
│   │   └── quickstart.mdx
│   ├── mdx-components.jsx
│   ├── next.config.js
│   ├── package.json
│   ├── public/
│   │   ├── favicons/
│   │   │   └── site.webmanifest
│   │   └── llms.txt
│   └── theme.config.jsx
├── extensions/
│   ├── chatwoot/
│   │   ├── .env_sample
│   │   ├── __init__.py
│   │   └── app.py
│   ├── chrome/
│   │   ├── _locales/
│   │   │   └── en/
│   │   │       └── messages.json
│   │   ├── dist/
│   │   │   └── output.css
│   │   ├── js/
│   │   │   └── jquery/
│   │   │       ├── .gitignore
│   │   │       ├── README.md
│   │   │       ├── bower.json
│   │   │       ├── component.json
│   │   │       ├── composer.json
│   │   │       ├── jquery.js
│   │   │       └── package.json
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── popup.html
│   │   ├── popup.js
│   │   ├── src/
│   │   │   └── bg/
│   │   │       └── service-worker.js
│   │   ├── styles.css
│   │   └── tailwind.config.js
│   ├── discord/
│   │   ├── __init__.py
│   │   └── bot.py
│   ├── react-widget/
│   │   ├── .gitignore
│   │   ├── .parcelrc
│   │   ├── README.md
│   │   ├── custom.d.ts
│   │   ├── package.json
│   │   ├── publish.sh
│   │   ├── src/
│   │   │   ├── App.tsx
│   │   │   ├── browser.tsx
│   │   │   ├── components/
│   │   │   │   ├── DocsGPTWidget.tsx
│   │   │   │   └── SearchBar.tsx
│   │   │   ├── index.html
│   │   │   ├── index.ts
│   │   │   ├── main.tsx
│   │   │   ├── requests/
│   │   │   │   ├── searchAPI.ts
│   │   │   │   └── streamingApi.ts
│   │   │   ├── types/
│   │   │   │   └── index.ts
│   │   │   └── utils/
│   │   │       └── helper.ts
│   │   └── tsconfig.json
│   ├── slack-bot/
│   │   ├── .gitignore
│   │   ├── Readme.md
│   │   ├── app.py
│   │   └── requirements.txt
│   └── web-widget/
│       ├── README.md
│       ├── dist/
│       │   ├── chat-widget.js
│       │   └── output.css
│       ├── index.html
│       ├── package.json
│       ├── src/
│       │   ├── html/
│       │   │   └── widget.html
│       │   ├── input.css
│       │   └── js/
│       │       └── script.js
│       └── tailwind.config.js
├── frontend/
│   ├── .husky/
│   │   └── pre-commit
│   ├── .prettierignore
│   ├── Dockerfile
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.cjs
│   ├── prettier.config.cjs
│   ├── src/
│   │   ├── App.tsx
│   │   ├── Hero.tsx
│   │   ├── Navigation.tsx
│   │   ├── PageNotFound.tsx
│   │   ├── agents/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── AgentLogs.tsx
│   │   │   ├── AgentPreview.tsx
│   │   │   ├── AgentsList.tsx
│   │   │   ├── FolderCard.tsx
│   │   │   ├── NewAgent.tsx
│   │   │   ├── SharedAgent.tsx
│   │   │   ├── SharedAgentCard.tsx
│   │   │   ├── SharedAgentGate.tsx
│   │   │   ├── WorkflowBuilder.tsx
│   │   │   ├── agentPreviewSlice.ts
│   │   │   ├── agents.config.ts
│   │   │   ├── components/
│   │   │   │   └── AgentTypeModal.tsx
│   │   │   ├── hooks/
│   │   │   │   ├── useAgentSearch.ts
│   │   │   │   └── useAgentsFetch.ts
│   │   │   ├── index.tsx
│   │   │   ├── types/
│   │   │   │   ├── index.ts
│   │   │   │   └── workflow.ts
│   │   │   └── workflow/
│   │   │       ├── WorkflowBuilder.tsx
│   │   │       ├── WorkflowPreview.tsx
│   │   │       ├── components/
│   │   │       │   ├── MobileBlocker.tsx
│   │   │       │   └── PromptTextArea.tsx
│   │   │       ├── nodes/
│   │   │       │   ├── BaseNode.tsx
│   │   │       │   ├── ConditionNode.tsx
│   │   │       │   ├── SetStateNode.tsx
│   │   │       │   └── index.tsx
│   │   │       └── workflowPreviewSlice.ts
│   │   ├── api/
│   │   │   ├── client.ts
│   │   │   ├── endpoints.ts
│   │   │   └── services/
│   │   │       ├── conversationService.ts
│   │   │       ├── modelService.ts
│   │   │       └── userService.ts
│   │   ├── components/
│   │   │   ├── Accordion.tsx
│   │   │   ├── ActionButtons.tsx
│   │   │   ├── AgentImage.tsx
│   │   │   ├── ArtifactSidebar.tsx
│   │   │   ├── Avatar.tsx
│   │   │   ├── Chunks.tsx
│   │   │   ├── ConfigFields.tsx
│   │   │   ├── ConnectedStateSkeleton.tsx
│   │   │   ├── ConnectorAuth.tsx
│   │   │   ├── ConnectorTree.tsx
│   │   │   ├── ContextMenu.tsx
│   │   │   ├── CopyButton.tsx
│   │   │   ├── DocumentPagination.tsx
│   │   │   ├── Dropdown.tsx
│   │   │   ├── DropdownMenu.tsx
│   │   │   ├── DropdownModel.tsx
│   │   │   ├── FilePicker.tsx
│   │   │   ├── FileSelectionSkeleton.tsx
│   │   │   ├── FileTree.tsx
│   │   │   ├── FileUpload.tsx
│   │   │   ├── GoogleDrivePicker.tsx
│   │   │   ├── Head.tsx
│   │   │   ├── Help.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── MermaidRenderer.tsx
│   │   │   ├── MessageInput.tsx
│   │   │   ├── MultiSelectPopup.tsx
│   │   │   ├── Notification.tsx
│   │   │   ├── RetryIcon.tsx
│   │   │   ├── SearchableDropdown.tsx
│   │   │   ├── SendArrowIcon.tsx
│   │   │   ├── SettingsBar.tsx
│   │   │   ├── Sidebar.tsx
│   │   │   ├── SkeletonLoader.tsx
│   │   │   ├── SourcesPopup.tsx
│   │   │   ├── Spinner.tsx
│   │   │   ├── Table.tsx
│   │   │   ├── TextToSpeechButton.tsx
│   │   │   ├── ToggleSwitch.tsx
│   │   │   ├── ToolsPopup.tsx
│   │   │   ├── UploadToast.tsx
│   │   │   ├── types/
│   │   │   │   ├── Dropdown.types.ts
│   │   │   │   └── index.ts
│   │   │   └── ui/
│   │   │       ├── alert.tsx
│   │   │       ├── button.tsx
│   │   │       ├── command.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── multi-select.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── select.tsx
│   │   │       └── sheet.tsx
│   │   ├── constants/
│   │   │   └── fileUpload.ts
│   │   ├── conversation/
│   │   │   ├── Conversation.tsx
│   │   │   ├── ConversationBubble.module.css
│   │   │   ├── ConversationBubble.tsx
│   │   │   ├── ConversationMessages.tsx
│   │   │   ├── ConversationTile.tsx
│   │   │   ├── SharedConversation.tsx
│   │   │   ├── conversationHandlers.ts
│   │   │   ├── conversationModels.ts
│   │   │   ├── conversationSlice.ts
│   │   │   ├── sharedConversationSlice.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── hooks/
│   │   │   ├── index.ts
│   │   │   ├── useDataInitializer.ts
│   │   │   └── useTokenAuth.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── locale/
│   │   │   ├── de.json
│   │   │   ├── en.json
│   │   │   ├── es.json
│   │   │   ├── i18n.ts
│   │   │   ├── jp.json
│   │   │   ├── ru.json
│   │   │   ├── zh-TW.json
│   │   │   └── zh.json
│   │   ├── main.tsx
│   │   ├── modals/
│   │   │   ├── AddActionModal.tsx
│   │   │   ├── AddToolModal.tsx
│   │   │   ├── AgentDetailsModal.tsx
│   │   │   ├── ConfigToolModal.tsx
│   │   │   ├── ConfirmationModal.tsx
│   │   │   ├── DeleteConvModal.tsx
│   │   │   ├── FolderManagementModal.tsx
│   │   │   ├── ImportSpecModal.tsx
│   │   │   ├── JWTModal.tsx
│   │   │   ├── MCPServerModal.tsx
│   │   │   ├── MoveToFolderModal.tsx
│   │   │   ├── ShareConversationModal.tsx
│   │   │   ├── WrapperModal.tsx
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── models/
│   │   │   ├── misc.ts
│   │   │   └── types.ts
│   │   ├── preferences/
│   │   │   ├── PromptsModal.tsx
│   │   │   ├── preferenceApi.ts
│   │   │   ├── preferenceSlice.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── settings/
│   │   │   ├── Analytics.tsx
│   │   │   ├── General.tsx
│   │   │   ├── Logs.tsx
│   │   │   ├── Prompts.tsx
│   │   │   ├── Sources.tsx
│   │   │   ├── ToolConfig.tsx
│   │   │   ├── Tools.tsx
│   │   │   ├── index.tsx
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── store.ts
│   │   ├── upload/
│   │   │   ├── Upload.tsx
│   │   │   ├── types/
│   │   │   │   └── ingestor.ts
│   │   │   └── uploadSlice.ts
│   │   ├── utils/
│   │   │   ├── browserUtils.ts
│   │   │   ├── chartUtils.ts
│   │   │   ├── dateTimeUtils.ts
│   │   │   ├── objectUtils.ts
│   │   │   ├── providerUtils.ts
│   │   │   └── stringUtils.ts
│   │   └── vite-env.d.ts
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
├── md-gen.py
├── pytest.ini
├── scripts/
│   ├── migrate_conversation_id_dbref_to_objectid.py
│   └── migrate_to_v1_vectorstore.py
├── setup.ps1
├── setup.sh
└── tests/
    ├── __init__.py
    ├── agents/
    │   ├── __init__.py
    │   ├── test_agent_creator.py
    │   ├── test_base_agent.py
    │   ├── test_classic_agent.py
    │   ├── test_get_artifact.py
    │   ├── test_react_agent.py
    │   ├── test_tool_action_parser.py
    │   ├── test_tool_manager.py
    │   ├── test_workflow_engine.py
    │   └── test_workflow_template.py
    ├── api/
    │   ├── __init__.py
    │   ├── answer/
    │   │   ├── __init__.py
    │   │   ├── routes/
    │   │   │   ├── __init__.py
    │   │   │   ├── test_base.py
    │   │   │   └── test_search.py
    │   │   └── services/
    │   │       ├── __init__.py
    │   │       ├── test_conversation_service.py
    │   │       ├── test_prompt_renderer.py
    │   │       └── test_stream_processor.py
    │   ├── conftest.py
    │   └── user/
    │       ├── attachments/
    │       │   └── test_routes.py
    │       ├── sources/
    │       │   ├── __init__.py
    │       │   ├── test_audio_upload.py
    │       │   └── test_routes.py
    │       ├── test_base.py
    │       └── test_exception_sanitization.py
    ├── conftest.py
    ├── core/
    │   └── test_url_validation.py
    ├── integration/
    │   ├── __init__.py
    │   ├── base.py
    │   ├── run_all.py
    │   ├── test_agents.py
    │   ├── test_analytics.py
    │   ├── test_chat.py
    │   ├── test_connectors.py
    │   ├── test_conversations.py
    │   ├── test_mcp.py
    │   ├── test_misc.py
    │   ├── test_prompts.py
    │   ├── test_sources.py
    │   └── test_tools.py
    ├── llm/
    │   ├── handlers/
    │   │   ├── test_google.py
    │   │   ├── test_handler_creator.py
    │   │   ├── test_llm_handlers.py
    │   │   └── test_openai.py
    │   ├── test_anthropic_llm.py
    │   ├── test_google_llm.py
    │   ├── test_openai_llm.py
    │   └── test_sagemaker.py
    ├── parser/
    │   ├── file/
    │   │   ├── test_audio_parser.py
    │   │   ├── test_docs_parser.py
    │   │   ├── test_embedding_pipeline.py
    │   │   ├── test_epub_parser.py
    │   │   ├── test_html_parser.py
    │   │   ├── test_image_parser.py
    │   │   ├── test_json_parser.py
    │   │   ├── test_markdown_parser.py
    │   │   ├── test_pptx_parser.py
    │   │   ├── test_rst_parser.py
    │   │   └── test_tabular_parser.py
    │   └── remote/
    │       ├── test_crawler_loader.py
    │       ├── test_crawler_markdown.py
    │       ├── test_github_loader.py
    │       ├── test_reddit_loader.py
    │       ├── test_s3_loader.py
    │       ├── test_share_point_loader.py
    │       └── test_web_loader.py
    ├── requirements.txt
    ├── security/
    │   └── test_encryption.py
    ├── storage/
    │   ├── test_local_storage.py
    │   └── test_s3_storage.py
    ├── stt/
    │   ├── test_live_session.py
    │   ├── test_stt_creator.py
    │   └── test_upload_limits.py
    ├── test_agent_token_tracking.py
    ├── test_app.py
    ├── test_attachment_worker_audio.py
    ├── test_cache.py
    ├── test_celery.py
    ├── test_compression_service.py
    ├── test_error.py
    ├── test_integration.py
    ├── test_memory_tool.py
    ├── test_model_validation.py
    ├── test_notes_tool.py
    ├── test_openapi3.yaml
    ├── test_openapi3parser.py
    ├── test_todo_tool.py
    ├── test_token_management.py
    ├── test_usage.py
    ├── test_zip_extraction_security.py
    └── tts/
        ├── test_elevenlabs_tts.py
        ├── test_google_tts.py
        └── test_tts_creator.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
FROM python:3.12-bookworm

# Install Node.js 20.x
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
    && apt-get install -y nodejs \
    && rm -rf /var/lib/apt/lists/*

# Install global npm packages
RUN npm install -g husky vite

# Create and activate Python virtual environment
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

WORKDIR /workspace

================================================
FILE: .devcontainer/devc-welcome.md
================================================
# Welcome to DocsGPT Devcontainer

Welcome to the DocsGPT development environment! This guide will help you get started quickly.

## Starting Services

To run DocsGPT, you need to start three main services: Flask (backend), Celery (task queue), and Vite (frontend). Here are the commands to start each service within the devcontainer:

### Vite (Frontend)

```bash
cd frontend
npm run dev -- --host
```

### Flask (Backend)

```bash
flask --app application/app.py run --host=0.0.0.0 --port=7091
```

### Celery (Task Queue)

```bash
celery -A application.app.celery worker -l INFO
```

## Github Codespaces Instructions

### 1. Make Ports Public:

Go to the "Ports" panel in Codespaces (usually located at the bottom of the VS Code window).

For both port 5173 and 7091, right-click on the port and select "Make Public".

![CleanShot 2025-02-12 at 09 46 14@2x](https://github.com/user-attachments/assets/00a34b16-a7ef-47af-9648-87a7e3008475)


 ### 2. Update VITE_API_HOST:

After making port 7091 public, copy the public URL provided by Codespaces for port 7091.

Open the file frontend/.env.development.

Find the line VITE_API_HOST=http://localhost:7091.

Replace http://localhost:7091 with the public URL you copied from Codespaces.

![CleanShot 2025-02-12 at 09 46 56@2x](https://github.com/user-attachments/assets/c472242f-1079-4cd8-bc0b-2d78db22b94c)


================================================
FILE: .devcontainer/devcontainer.json
================================================
{
	"name": "DocsGPT Dev Container",
	"dockerComposeFile": ["docker-compose-dev.yaml", "docker-compose.override.yaml"],
	"service": "dev",
	"workspaceFolder": "/workspace",
	"postCreateCommand": ".devcontainer/post-create-command.sh",
	"forwardPorts": [7091, 5173, 6379, 27017],
	"customizations": {
	  "vscode": {
		"extensions": [
		  "ms-python.python",
		  "ms-toolsai.jupyter",
		  "esbenp.prettier-vscode",
		  "dbaeumer.vscode-eslint"
		]
	  },
	  "codespaces": {
			"openFiles": [
			".devcontainer/devc-welcome.md",
			"CONTRIBUTING.md"
			]
		}
	}
  }

================================================
FILE: .devcontainer/docker-compose-dev.yaml
================================================
services:

  redis:
    image: redis:6-alpine
    ports:
      - 6379:6379

  mongo:
    image: mongo:6
    ports:
      - 27017:27017
    volumes:
      - mongodb_data_container:/data/db



volumes:
  mongodb_data_container:

================================================
FILE: .devcontainer/docker-compose.override.yaml
================================================
version: '3.8'

services:
  dev:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ../:/workspace:cached
    command: sleep infinity
    depends_on:
      redis:
        condition: service_healthy
      mongo:
        condition: service_healthy
    environment:
      - CELERY_BROKER_URL=redis://redis:6379/0
      - CELERY_RESULT_BACKEND=redis://redis:6379/1
      - MONGO_URI=mongodb://mongo:27017/docsgpt
      - CACHE_REDIS_URL=redis://redis:6379/2
    networks:
      - default

  redis:
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 30s
      retries: 5

  mongo:
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 5s
      timeout: 30s
      retries: 5

networks:
  default:
    name: docsgpt-dev-network

================================================
FILE: .devcontainer/post-create-command.sh
================================================
#!/bin/bash

set -e  # Exit immediately if a command exits with a non-zero status

if [ ! -f frontend/.env.development ]; then
  cp -n .env-template frontend/.env.development || true # Assuming .env-template is in the root
fi

# Determine VITE_API_HOST based on environment
if [ -n "$CODESPACES" ]; then
  # Running in Codespaces
  CODESPACE_NAME=$(echo "$CODESPACES" | cut -d'-' -f1) # Extract codespace name
  PUBLIC_API_HOST="https://${CODESPACE_NAME}-7091.${GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
  echo "Setting VITE_API_HOST for Codespaces: $PUBLIC_API_HOST in frontend/.env.development"
  sed -i "s|VITE_API_HOST=.*|VITE_API_HOST=$PUBLIC_API_HOST|" frontend/.env.development
else
  # Not running in Codespaces (local devcontainer)
  DEFAULT_API_HOST="http://localhost:7091"
  echo "Setting VITE_API_HOST for local dev: $DEFAULT_API_HOST in frontend/.env.development"
  sed -i "s|VITE_API_HOST=.*|VITE_API_HOST=$DEFAULT_API_HOST|" frontend/.env.development
fi


mkdir -p model
if [ ! -d model/all-mpnet-base-v2 ]; then
    wget -q https://d3dg1063dc54p9.cloudfront.net/models/embeddings/mpnet-base-v2.zip -O model/mpnet-base-v2.zip
    unzip -q model/mpnet-base-v2.zip -d model
    rm model/mpnet-base-v2.zip
fi
pip install -r application/requirements.txt
cd frontend
npm install --include=dev

================================================
FILE: .env-template
================================================
API_KEY=<LLM api key (for example, open ai key)>
LLM_NAME=docsgpt
VITE_API_STREAMING=true
INTERNAL_KEY=<internal key for worker-to-backend authentication>

# Remote Embeddings (Optional - for using a remote embeddings API instead of local SentenceTransformer)
# When set, the app will use the remote API and won't load SentenceTransformer (saves RAM)
EMBEDDINGS_BASE_URL=
EMBEDDINGS_KEY=

#For Azure (you can delete it if you don't use Azure)
OPENAI_API_BASE=
OPENAI_API_VERSION=
AZURE_DEPLOYMENT_NAME=
AZURE_EMBEDDINGS_DEPLOYMENT_NAME=

#Azure AD Application (client) ID
MICROSOFT_CLIENT_ID=your-azure-ad-client-id
#Azure AD Application client secret
MICROSOFT_CLIENT_SECRET=your-azure-ad-client-secret
#Azure AD Tenant ID (or 'common' for multi-tenant)
MICROSOFT_TENANT_ID=your-azure-ad-tenant-id
#If you are using a Microsoft Entra ID tenant,
#configure the AUTHORITY variable as
#"https://login.microsoftonline.com/TENANT_GUID"
#or "https://login.microsoftonline.com/contoso.onmicrosoft.com".
#Alternatively, use "https://login.microsoftonline.com/common" for multi-tenant app.
MICROSOFT_AUTHORITY=https://{tenantId}.ciamlogin.com/{tenantId}


================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: arc53


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.yml
================================================
name: "🐛 Bug Report"
description: "Submit a bug report to help us improve"
title: "🐛 Bug Report: "
labels: ["type: bug"]
body:
  - type: markdown
    attributes:
      value: We value your time and your efforts to submit this bug report is appreciated. 🙏

  - type: textarea
    id: description
    validations:
      required: true
    attributes:
      label: "📜 Description"
      description: "A clear and concise description of what the bug is."
      placeholder: "It bugs out when ..."

  - type: textarea
    id: steps-to-reproduce
    validations:
      required: true
    attributes:
      label: "👟 Reproduction steps"
      description: "How do you trigger this bug? Please walk us through it step by step."
      placeholder: "1. Go to '...'
        2. Click on '....'
        3. Scroll down to '....'
        4. See error"

  - type: textarea
    id: expected-behavior
    validations:
      required: true
    attributes:
      label: "👍 Expected behavior"
      description: "What did you think should happen?"
      placeholder: "It should ..."

  - type: textarea
    id: actual-behavior
    validations:
      required: true
    attributes:
      label: "👎 Actual Behavior with Screenshots"
      description: "What did actually happen? Add screenshots, if applicable."
      placeholder: "It actually ..."

  - type: dropdown
    id: operating-system
    attributes:
      label: "💻 Operating system"
      description: "What OS is your app running on?"
      options:
        - Linux
        - MacOS
        - Windows
        - Something else
    validations:
      required: true

  - type: dropdown
    id: browsers
    attributes:
      label: What browsers are you seeing the problem on?
      multiple: true
      options:
        - Firefox
        - Chrome
        - Safari
        - Microsoft Edge
        - Something else

  - type: dropdown
    id: dev-environment
    validations:
      required: true
    attributes:
      label: "🤖 What development environment are you experiencing this bug on?"
      options:
        - Docker
        - Local dev server

  - type: textarea
    id: env-vars
    validations:
      required: false
    attributes:
      label: "🔒 Did you set the correct environment variables in the right path? List the environment variable names (not values please!)"
      description: "Please refer to the [Project setup instructions](https://github.com/arc53/DocsGPT#quickstart) if you are unsure."
      placeholder: "It actually ..."

  - type: textarea
    id: additional-context
    validations:
      required: false
    attributes:
      label: "📃 Provide any additional context for the Bug."
      description: "Add any other context about the problem here."
      placeholder: "It actually ..."

  - type: textarea
    id: logs
    validations:
      required: false
    attributes:
      label: 📖 Relevant log output
      description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
      render: shell

  - type: checkboxes
    id: no-duplicate-issues
    attributes:
      label: "👀 Have you spent some time to check if this bug has been raised before?"
      options:
        - label: "I checked and didn't find similar issue"
          required: true

  - type: dropdown
    id: willing-to-submit-pr
    attributes:
      label: 🔗 Are you willing to submit PR?
      description: This is absolutely not required, but we are happy to guide you in the contribution process.
      options: # Added options key
        - "Yes, I am willing to submit a PR!"
        - "No"
    validations:
      required: false


  - type: checkboxes
    id: terms
    attributes:
      label: 🧑‍⚖️ Code of Conduct
      description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/arc53/DocsGPT/blob/main/CODE_OF_CONDUCT.md)
      options:
        - label: I agree to follow this project's Code of Conduct
          required: true


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.yml
================================================
name: 🚀 Feature
description: "Submit a proposal for a new feature"
title: "🚀 Feature: "
labels: [feature]
body:
  - type: markdown
    attributes:
      value: We value your time and your efforts to submit this bug report is appreciated. 🙏
  - type: textarea
    id: feature-description
    validations:
      required: true
    attributes:
      label: "🔖 Feature description"
      description: "A clear and concise description of what the feature is."
      placeholder: "You should add ..."
  - type: textarea
    id: pitch
    validations:
      required: true
    attributes:
      label: "🎤 Why is this feature needed ?"
      description: "Please explain why this feature should be implemented and how it would be used. Add examples, if applicable."
      placeholder: "In my use-case, ..."
  - type: textarea
    id: solution
    validations:
      required: true
    attributes:
      label: "✌️ How do you aim to achieve this?"
      description: "A clear and concise description of what you want to happen."
      placeholder: "I want this feature to, ..."
  - type: textarea
    id: alternative
    validations:
      required: false
    attributes:
      label: "🔄️ Additional Information"
      description: "A clear and concise description of any alternative solutions or additional solutions you've considered."
      placeholder: "I tried, ..."
  - type: checkboxes
    id: no-duplicate-issues
    attributes:
      label: "👀 Have you spent some time to check if this feature request has been raised before?"
      options:
        - label: "I checked and didn't find similar issue"
          required: true
  - type: dropdown
    id: willing-to-submit-pr
    attributes:
      label: Are you willing to submit PR?
      description: This is absolutely not required, but we are happy to guide you in the contribution process.
      options:
        - "Yes I am willing to submit a PR!"


================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)

- **Why was this change needed?** (You can also link to an open issue here)

- **Other information**:

================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "pip" # See documentation for possible values
    directory: "/application" # Location of package manifests
    schedule:
      interval: "daily"
  - package-ecosystem: "npm" # See documentation for possible values
    directory: "/frontend" # Location of package manifests
    schedule:
      interval: "daily"
  - package-ecosystem: "npm"
    directory: "/extensions/react-widget"
    schedule:
      interval: "daily"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "daily"

================================================
FILE: .github/holopin.yml
================================================
organization: docsgpt
defaultSticker: cm1ulwkkl180570cl82rtzympu
stickers:
  - id: cm1ulwkkl180570cl82rtzympu
    alias: contributor2024
  - id: cm1ureg8o130450cl8c1po6mil
    alias: api
  - id: cm1urhmag148240cl8yvqxkthx
    alias: lpc
  - id: cm1urlcpq622090cl2tvu4w71y
    alias: lexeu


================================================
FILE: .github/labeler.yml
================================================
repo:
- changed-files:
  - any-glob-to-any-file: '*'

github:
- changed-files:
  - any-glob-to-any-file: '.github/**/*'

application:
- changed-files:
  - any-glob-to-any-file: 'application/**/*'

docs:
- changed-files:
  - any-glob-to-any-file: 'docs/**/*'

extensions:
- changed-files:
  - any-glob-to-any-file: 'extensions/**/*'

frontend:
- changed-files:
  - any-glob-to-any-file: 'frontend/**/*'

scripts:
- changed-files:
  - any-glob-to-any-file: 'scripts/**/*'

tests:
- changed-files:
  - any-glob-to-any-file: 'tests/**/*'


================================================
FILE: .github/styles/DocsGPT/Spelling.yml
================================================
extends: spelling
level: warning
message: "Did you really mean '%s'?"
ignore:
  - "**/node_modules/**"
  - "**/dist/**"
  - "**/build/**"
  - "**/coverage/**"
  - "**/public/**"
  - "**/static/**"
vocab: DocsGPT


================================================
FILE: .github/styles/config/vocabularies/DocsGPT/accept.txt
================================================
Ollama
Qdrant
Milvus
Chatwoot
Nextra
VSCode
npm
LLMs
APIs
Groq
SGLang
LMDeploy
OAuth
Vite
LLM
JSONPath
UIs
configs
uncomment
qdrant
vectorstore
docsgpt
llm
GPUs
kubectl
Lightsail
enqueues
chatbot
VSCode's
Shareability
feedbacks
automations
Premade
Signup
Repo
repo
env
URl
agentic
llama_cpp
parsable
SDKs
boolean
bool
hardcode
EOL


================================================
FILE: .github/workflows/bandit.yaml
================================================
name: Bandit Security Scan

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  bandit_scan:
    if: ${{ github.repository == 'arc53/DocsGPT' }}
    runs-on: ubuntu-latest
    permissions:
      security-events: write
      actions: read
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install bandit  # Bandit is needed for this action
          if [ -f application/requirements.txt ]; then pip install -r application/requirements.txt; fi

      - name: Run Bandit scan
        uses: PyCQA/bandit-action@v1
        with:
          severity: medium
          confidence: medium
          targets: application/
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

================================================
FILE: .github/workflows/ci.yml
================================================
name: Build and push DocsGPT Docker image

on:
  release:
    types: [published]

jobs:
  build:
    if: github.repository == 'arc53/DocsGPT'
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
            suffix: amd64
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
            suffix: arm64
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Set up QEMU  # Only needed for emulation, not for native arm64 builds
        if: matrix.platform == 'linux/arm64'
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push platform-specific images
        uses: docker/build-push-action@v6
        with:
          file: './application/Dockerfile'
          platforms: ${{ matrix.platform }}
          context: ./application
          push: true
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
            ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
          provenance: false
          sbom: false
          cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:latest
          cache-to: type=inline

  manifest:
    if: github.repository == 'arc53/DocsGPT'
    needs: build
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create and push manifest for DockerHub
        run: |
          set -e
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }} \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:latest \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:${{ github.event.release.tag_name }}-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:latest

      - name: Create and push manifest for ghcr.io
        run: |
          set -e
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }} \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:latest \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:${{ github.event.release.tag_name }}-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:latest

================================================
FILE: .github/workflows/cife.yml
================================================
name: Build and push DocsGPT-FE Docker image

on:
  release:
    types: [published]

jobs:
  build:
    if: github.repository == 'arc53/DocsGPT'
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
            suffix: amd64
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
            suffix: arm64
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Set up QEMU # Only needed for emulation, not for native arm64 builds
        if: matrix.platform == 'linux/arm64'
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push platform-specific images
        uses: docker/build-push-action@v6
        with:
          file: './frontend/Dockerfile'
          platforms: ${{ matrix.platform }}
          context: ./frontend
          push: true
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
            ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}-${{ matrix.suffix }}
          provenance: false
          sbom: false
          cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:latest
          cache-to: type=inline

  manifest:
    if: github.repository == 'arc53/DocsGPT'
    needs: build
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create and push manifest for DockerHub
        run: |
          set -e
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }} \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:latest \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:${{ github.event.release.tag_name }}-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:latest

      - name: Create and push manifest for ghcr.io
        run: |
          set -e
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }} \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt-fe:latest \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:${{ github.event.release.tag_name }}-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt-fe:latest

================================================
FILE: .github/workflows/docker-develop-build.yml
================================================
name: Build and push multi-arch DocsGPT Docker image

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  build:
    if: github.repository == 'arc53/DocsGPT'
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
            suffix: amd64
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
            suffix: arm64
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push platform-specific images
        uses: docker/build-push-action@v6
        with:
          file: './application/Dockerfile'
          platforms: ${{ matrix.platform }}
          context: ./application
          push: true
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-${{ matrix.suffix }}
            ghcr.io/${{ github.repository_owner }}/docsgpt:develop-${{ matrix.suffix }}
          provenance: false
          sbom: false
          cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt:develop
          cache-to: type=inline

  manifest:
    if: github.repository == 'arc53/DocsGPT'
    needs: build
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}
        
      - name: Create and push manifest for DockerHub
        run: |
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt:develop

      - name: Create and push manifest for ghcr.io
        run: |
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt:develop \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:develop-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt:develop-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt:develop

================================================
FILE: .github/workflows/docker-develop-fe-build.yml
================================================
name: Build and push DocsGPT FE Docker image for development

on:
  workflow_dispatch:
  push:
    branches:
      - main

jobs:
  build:
    if: github.repository == 'arc53/DocsGPT'
    strategy:
      matrix:
        include:
          - platform: linux/amd64
            runner: ubuntu-latest
            suffix: amd64
          - platform: linux/arm64
            runner: ubuntu-24.04-arm
            suffix: arm64
    runs-on: ${{ matrix.runner }}
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4

      - name: Set up QEMU # Only needed for emulation, not for native arm64 builds
        if: matrix.platform == 'linux/arm64'
        uses: docker/setup-qemu-action@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push platform-specific images
        uses: docker/build-push-action@v6
        with:
          file: './frontend/Dockerfile'
          platforms: ${{ matrix.platform }}
          context: ./frontend
          push: true
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop-${{ matrix.suffix }}
            ghcr.io/${{ github.repository_owner }}/docsgpt-fe:develop-${{ matrix.suffix }}
          provenance: false
          sbom: false
          cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop
          cache-to: type=inline

  manifest:
    if: github.repository == 'arc53/DocsGPT'
    needs: build
    runs-on: ubuntu-latest
    permissions:
      packages: write
    steps:
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
        with:
          driver: docker-container
          install: true

      - name: Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Login to ghcr.io
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Create and push manifest for DockerHub
        run: |
          docker manifest create ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop-amd64 \
            --amend ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop-arm64
          docker manifest push ${{ secrets.DOCKER_USERNAME }}/docsgpt-fe:develop

      - name: Create and push manifest for ghcr.io
        run: |
          docker manifest create ghcr.io/${{ github.repository_owner }}/docsgpt-fe:develop \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:develop-amd64 \
            --amend ghcr.io/${{ github.repository_owner }}/docsgpt-fe:develop-arm64
          docker manifest push ghcr.io/${{ github.repository_owner }}/docsgpt-fe:develop

================================================
FILE: .github/workflows/labeler.yml
================================================
# https://github.com/actions/labeler
name: Pull Request Labeler
on:
  - pull_request_target
jobs:
  triage:
    if: github.repository == 'arc53/DocsGPT'
    permissions:
      contents: read
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v5
        with:
          repo-token: "${{ secrets.GITHUB_TOKEN }}"
          sync-labels: true


================================================
FILE: .github/workflows/lint.yml
================================================
name: Python linting

on:
  push:
    branches:
      - '*'
  pull_request:
    types: [ opened, synchronize ]

permissions:
  contents: read

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Lint with Ruff
        uses: chartboost/ruff-action@v1


================================================
FILE: .github/workflows/pytest.yml
================================================
name: Run python tests with pytest
on: [push, pull_request]

permissions:
  contents: read

jobs:
  pytest_and_coverage:
    name: Run tests and count coverage
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12"]
    steps:
      - uses: actions/checkout@v4
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          cd application
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
          cd ../tests
          if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
      - name: Test with pytest and generate coverage report
        run: |
          python -m pytest --cov=application --cov-report=xml --cov-report=term-missing
      - name: Upload coverage reports to Codecov
        if: github.event_name == 'pull_request' && matrix.python-version == '3.12'
        uses: codecov/codecov-action@v5
        env:
          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}


================================================
FILE: .github/workflows/sync_fork.yaml
================================================
name: Upstream Sync

permissions:
  contents: write

on:
  schedule:
    - cron: "0 0 * * *" # every hour
  workflow_dispatch:

jobs:
  sync_latest_from_upstream:
    name: Sync latest commits from upstream repo
    runs-on: ubuntu-latest
    if: ${{ github.event.repository.fork }}

    steps:
      # Step 1: run a standard checkout action
      - name: Checkout target repo
        uses: actions/checkout@v4

      # Step 2: run the sync action
      - name: Sync upstream changes
        id: sync
        uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
        with:
          # set your upstream repo and branch
          upstream_sync_repo: arc53/DocsGPT
          upstream_sync_branch: main
          target_sync_branch: main
          target_repo_token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, no need to set

          # Set test_mode true to run tests instead of the true action!!
          test_mode: false

      - name: Sync check
        if: failure()
        run: |
          echo "::error::由于权限不足,导致同步失败(这是预期的行为),请前往仓库首页手动执行[Sync fork]。"
          echo "::error::Due to insufficient permissions, synchronization failed (as expected). Please go to the repository homepage and manually perform [Sync fork]."
          exit 1

================================================
FILE: .github/workflows/vale.yml
================================================
name: Vale Documentation Linter

on:
  pull_request:
    paths:
      - 'docs/**/*.md'
      - 'docs/**/*.mdx'
      - '**/*.md'
      - '.vale.ini'
      - '.github/styles/**'

permissions:
  contents: read
  pull-requests: write

jobs:
  vale:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Vale linter
        uses: errata-ai/vale-action@v2
        with:
          files: docs
          fail_on_error: false
          version: 3.0.5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
experiments/

experiments
# C extensions
*.so
*.next
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/
docs/public/_pagefind/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints
**/*.ipynb

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.flaskenv
# Spyder project settings
.spyderproject
.spyproject
.jwt_secret_key

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

#pycharm
.idea/

# macOS
.DS_Store

#frontend
# Logs
frontend/logs
frontend/*.log
frontend/npm-debug.log*
frontend/yarn-debug.log*
frontend/yarn-error.log*
frontend/pnpm-debug.log*
frontend/lerna-debug.log*

# Keep frontend utility helpers tracked (overrides global lib/ ignore)
!frontend/src/lib/
!frontend/src/lib/**

frontend/node_modules
frontend/dist
frontend/dist-ssr
frontend/*.local

# Editor directories and files
frontend/.vscode/*
frontend/!.vscode/extensions.json
frontend/.idea
frontend/.DS_Store
frontend/*.suo
frontend/*.ntvs*
frontend/*.njsproj
frontend/*.sln
frontend/*.sw?

application/vectors/

**/inputs

**/indexes

**/temp

**/yarn.lock

node_modules/
.vscode/settings.json
/models/
model/


================================================
FILE: .ruff.toml
================================================
# Allow lines to be as long as 120 characters.
line-length = 120

[lint.per-file-ignores]
# Integration tests use sys.path.insert() before imports for standalone execution
"tests/integration/*.py" = ["E402"]

================================================
FILE: .vale.ini
================================================
MinAlertLevel = warning
StylesPath = .github/styles

[*.{md,mdx}]
BasedOnStyles = DocsGPT


================================================
FILE: .vscode/launch.json
================================================
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Frontend Debug (npm)",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev",
      "cwd": "${workspaceFolder}/frontend"
    },
    {
        "name": "Flask Debugger",
        "type": "debugpy",
        "request": "launch",
        "module": "flask",
        "env": {
            "FLASK_APP": "application/app.py",
            "PYTHONPATH": "${workspaceFolder}",
            "FLASK_ENV": "development",
            "FLASK_DEBUG": "1",
            "FLASK_RUN_PORT": "7091",
            "FLASK_RUN_HOST": "0.0.0.0"

        },
        "args": [
            "run",
            "--no-debugger"
        ],
        "cwd": "${workspaceFolder}",
    },
    {
      "name": "Celery Debugger",
      "type": "debugpy",
      "request": "launch",
      "module": "celery",
      "env": {
        "PYTHONPATH": "${workspaceFolder}",
      },
      "args": [
        "-A",
        "application.app.celery",
        "worker",
        "-l",
        "INFO",
        "--pool=solo"
      ],
      "cwd": "${workspaceFolder}"
    },
    {
      "name": "Dev Containers (Mongo + Redis)",
      "type": "node-terminal",
      "request": "launch",
      "command": "docker compose -f deployment/docker-compose-dev.yaml up --build",
      "cwd": "${workspaceFolder}"
    }
  ],
  "compounds": [
    {
      "name": "DocsGPT: Full Stack",
      "configurations": [
        "Frontend Debug (npm)",
        "Flask Debugger",
        "Celery Debugger"
      ],
      "presentation": {
        "group": "DocsGPT",
        "order": 1
      }
    }
  ]
}

================================================
FILE: AGENTS.md
================================================
# AGENTS.md

- Read `CONTRIBUTING.md` before making non-trivial changes.
- For day-to-day development and feature work, follow the development-environment workflow rather than defaulting to `setup.sh` / `setup.ps1`.
- Avoid using the setup scripts during normal feature work unless the user explicitly asks for them. Users configure `.env` usually.
- Try to follow red/green TDD

### Check existing dev prerequisites first

For feature work, do **not** assume the environment needs to be recreated.

- Check whether the user already has a Python virtual environment such as `venv/` or `.venv/`.
- Check whether MongoDB is already running.
- Check whether Redis is already running.
- Reuse what is already working. Do not stop or recreate MongoDB, Redis, or the Python environment unless the task is environment setup or troubleshooting.

## Normal local development commands

Use these commands once the dev prerequisites above are satisfied.

### Backend

```bash
source .venv/bin/activate  # macOS/Linux
uv pip install -r application/requirements.txt  # or: pip install -r application/requirements.txt
```

Run the Flask API (if needed):

```bash
flask --app application/app.py run --host=0.0.0.0 --port=7091
```

Run the Celery worker in a separate terminal (if needed):

```bash
celery -A application.app.celery worker -l INFO
```

On macOS, prefer the solo pool for Celery:

```bash
python -m celery -A application.app.celery worker -l INFO --pool=solo
```

### Frontend

Install dependencies only when needed, then run the dev server:

```bash
cd frontend
npm install --include=dev
npm run dev
```

### Docs site

```bash
cd docs
npm install
```

### Python / backend changes validation

```bash
ruff check .
python -m pytest
```

### Frontend changes

```bash
cd frontend && npm run lint
cd frontend && npm run build
```

### Documentation changes

```bash
cd docs && npm run build
```

If Vale is installed locally and you edited prose, also run:

```bash
vale .
```

## Repository map

- `application/`: Flask backend, API routes, agent logic, retrieval, parsing, security, storage, Celery worker, and WSGI entrypoints.
- `tests/`: backend unit/integration tests and test-only Python dependencies.
- `frontend/`: Vite + React + TypeScript application.
- `frontend/src/`: main UI code, including `components`, `conversation`, `hooks`, `locale`, `settings`, `upload`, and Redux store wiring in `store.ts`.
- `docs/`: separate documentation site built with Next.js/Nextra.
- `extensions/`: integrations and widgets such as Chatwoot, Chrome, Discord, React widget, Slack bot, and web widget.
- `deployment/`: Docker Compose variants and Kubernetes manifests.

## Coding rules

### Backend

- Follow PEP 8 and keep Python line length at or under 120 characters.
- Use type hints for function arguments and return values.
- Add Google-style docstrings to new or substantially changed functions and classes.
- Add or update tests under `tests/` for backend behavior changes.
- Keep changes narrow in `api`, `auth`, `security`, `parser`, `retriever`, and `storage` areas.

### Backend Abstractions

- LLM providers implement a common interface in `application/llm/` (add new providers by extending the base class).
- Vector stores are abstracted in `application/vectorstore/`.
- Parsers live in `application/parser/` and handle different document formats in the ingestion stage.
- Agents and tools are in `application/agents/` and `application/agents/tools/`.
- Celery setup/config lives in `application/celery_init.py` and `application/celeryconfig.py`.
- Settings and env vars are managed via Pydantic in `application/core/settings.py`.

### Frontend

- Follow the existing ESLint + Prettier setup.
- Prefer small, reusable functional components and hooks.
- If shared state must be added, use Redux rather than introducing a new global state library.
- Avoid broad UI refactors unless the task explicitly asks for them.
- Do not re-create components if we already have some in the app.

## PR readiness

Before opening a PR:

- run the relevant validation commands above
- confirm backend changes still work end-to-end after ingesting sample data when applicable
- clearly summarize user-visible behavior changes
- mention any config, dependency, or deployment implications
- Ask your user to attach a screenshot or a video to it

================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors and leaders pledge to make participation in our
community, a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive and a healthy community.

## Our Standards

Examples of behavior that contribute to a positive environment for our
community include:

## Demonstrating empathy and kindness towards other people
1. Being respectful and open to differing opinions, viewpoints, and experiences
2. Giving and gracefully accepting constructive feedback
3. Taking accountability and offering apologies to those who have been impacted by our errors,
  while also gaining insights from the situation
4. Focusing on what is best not just for us as individuals but for the
  community as a whole

Examples of unacceptable behavior include:

1. The use of sexualized language or imagery, and sexual attention or
  advances of any kind
2. Trolling, insulting or derogatory comments, and personal or political attacks
3. Public or private harassment
4. Publishing other's private information, such as a physical or email
  address, without their explicit permission
5. Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
contact@arc53.com.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to be respectful towards the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action that they deem in violation of this Code of Conduct:

### 1. Correction
* **Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community space.

* **Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning
* **Community Impact**: A violation through a single incident or series
of actions.

* **Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban
* **Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

* **Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban
* **Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,harassment of an
individual or aggression towards or disparagement of classes of individuals.

* **Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


================================================
FILE: CONTRIBUTING.md
================================================
# Welcome to DocsGPT Contributing Guidelines

Thank you for choosing to contribute to DocsGPT! We are all very grateful! 

# We accept different types of contributions

📣 **Discussions** - Engage in conversations, start new topics, or help answer questions.

🐞 **Issues** - This is where we keep track of tasks. It could be bugs, fixes or suggestions for new features.

🛠️ **Pull requests** - Suggest changes to our repository, either by working on existing issues or adding new features.

📚 **Wiki** - This is where our documentation resides.


## 🐞 Issues and Pull requests

- We value contributions in the form of discussions or suggestions. We recommend taking a look at existing issues and our [roadmap](https://github.com/orgs/arc53/projects/2).


- If you're interested in contributing code, here are some important things to know:

- We have a frontend built on React (Vite) and a backend in Python.

> **Required for every PR:** Please attach screenshots or a short screen
> recording that shows the working version of your changes. This makes the
> requirement visible to reviewers and helps them quickly verify what you are
> submitting.

  
Before creating issues, please check out how the latest version of our app looks and works by launching it via [Quickstart](https://github.com/arc53/DocsGPT#quickstart) the version on our live demo is slightly modified with login. Your issues should relate to the version you can launch via [Quickstart](https://github.com/arc53/DocsGPT#quickstart).

### 👨‍💻 If you're interested in contributing code, here are some important things to know:

For instructions on setting up a development environment, please refer to our [Development Deployment Guide](https://docs.docsgpt.cloud/Deploying/Development-Environment).

Tech Stack Overview:

- 🌐 Frontend: Built with React (Vite) ⚛️,

- 🖥 Backend: Developed in Python 🐍

### 🌐 Frontend Contributions (⚛️ React, Vite)

*   The updated Figma design can be found [here](https://www.figma.com/file/OXLtrl1EAy885to6S69554/DocsGPT?node-id=0%3A1&t=hjWVuxRg9yi5YkJ9-1).  Please try to follow the guidelines.
*   **Coding Style:** We follow a strict coding style enforced by ESLint and Prettier. Please ensure your code adheres to the configuration provided in our repository's `fronetend/.eslintrc.js` file.  We recommend configuring your editor with ESLint and Prettier to help with this.
* **Component Structure:** Strive for small, reusable components.  Favor functional components and hooks over class components where possible.
* **State Management** If you need to add stores, please use Redux.

### 🖥 Backend Contributions (🐍 Python)

- Review our issues and contribute to [`/application`](https://github.com/arc53/DocsGPT/tree/main/application) 
- All new code should be covered with unit tests ([pytest](https://github.com/pytest-dev/pytest)). Please find tests under [`/tests`](https://github.com/arc53/DocsGPT/tree/main/tests) folder.
- Before submitting your Pull Request, ensure it can be queried after ingesting some test data.
- **Coding Style:** We adhere to the [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guide for Python code. We use `ruff` as our linter and code formatter.  Please ensure your code is formatted correctly and passes `ruff` checks before submitting.
- **Type Hinting:**  Please use type hints for all function arguments and return values. This improves code readability and helps catch errors early.  Example:

    ```python
    def my_function(name: str, count: int) -> list[str]:
        ...
    ```
- **Docstrings:**  All functions and classes should have docstrings explaining their purpose, parameters, and return values.  We prefer the [Google style docstrings](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html). Example:

    ```python
    def my_function(name: str, count: int) -> list[str]:
        """Does something with a name and a count.

        Args:
            name: The name to use.
            count: The number of times to do it.

        Returns:
            A list of strings.
        """
        ...
    ```
  
### Testing

To run unit tests from the root of the repository, execute:
```
python -m pytest
```

## Workflow 📈

Here's a step-by-step guide on how to contribute to DocsGPT:

1. **Fork the Repository:**
   - Click the "Fork" button at the top-right of this repository to create your fork.

2. **Clone the Forked Repository:**
   - Clone the repository using:
      ``` shell
      git clone https://github.com/<your-github-username>/DocsGPT.git
      ```

3. **Keep your Fork in Sync:**
   - Before you make any changes, make sure that your fork is in sync to avoid merge conflicts using:
     ```shell
     git remote add upstream https://github.com/arc53/DocsGPT.git
     git pull upstream main
     ```

4. **Create and Switch to a New Branch:**
   - Create a new branch for your contribution using:
     ```shell
     git checkout -b your-branch-name
     ```

5. **Make Changes:**
   - Make the required changes in your branch.

6. **Add Changes to the Staging Area:**
   - Add your changes to the staging area using:
     ```shell
     git add .
     ```

7. **Commit Your Changes:**
   - Commit your changes with a descriptive commit message using:
     ```shell
     git commit -m "Your descriptive commit message"
     ```

8. **Push Your Changes to the Remote Repository:**
   - Push your branch with changes to your fork on GitHub using:
     ```shell
     git push origin your-branch-name
     ```

9. **Submit a Pull Request (PR):**
   - Create a Pull Request from your branch to the main repository. Make sure to include a detailed description of your changes, reference any related issues, and attach screenshots or a screen recording showing the working version.

10. **Collaborate:**
   - Be responsive to comments and feedback on your PR.
   - Make necessary updates as suggested.
   - Once your PR is approved, it will be merged into the main repository.

11. **Testing:**
   - Before submitting a Pull Request, ensure your code passes all unit tests.
   - To run unit tests from the root of the repository, execute:
     ```shell
     python -m pytest
     ```

*Note: You should run the unit test only after making the changes to the backend code.*

12. **Questions and Collaboration:**
    - Feel free to join our Discord. We're very friendly and welcoming to new contributors, so don't hesitate to reach out.

Thank you for considering contributing to DocsGPT! 🙏

## Questions/collaboration
Feel free to join our [Discord](https://discord.gg/vN7YFfdMpj). We're very friendly and welcoming to new contributors, so don't hesitate to reach out.
# Thank you so much for considering to contributing DocsGPT!🙏


================================================
FILE: HACKTOBERFEST.md
================================================
# **🎉 Join the Hacktoberfest with DocsGPT and win a Free T-shirt for a meaningful PR! 🎉**

Welcome, contributors! We're excited to announce that DocsGPT is participating in Hacktoberfest. Get involved by submitting meaningful pull requests.

All Meaningful contributors with accepted PRs that were created for issues with the `hacktoberfest` label (set by our maintainer team: dartpain, siiddhantt, pabik, ManishMadan2882) will receive a cool T-shirt! 🤩.
<img width="1331" height="678" alt="hacktoberfest-mocks-preview" src="https://github.com/user-attachments/assets/633f6377-38db-48f5-b519-a8b3855a9eb4" />

Fill in [this form](https://forms.gle/Npaba4n9Epfyx56S8
) after your PR was merged please 

If you are in doubt don't hesitate to ping us on discord, ping me - Alex (dartpain).

## 📜 Here's How to Contribute:
```text
🛠️ Code: This is the golden ticket! Make meaningful contributions through PRs.

🧩 API extension: Build an app utilising DocsGPT API. We prefer submissions that showcase original ideas and turn the API into an AI agent.
They can be a completely separate repos. 
For example: 
https://github.com/arc53/tg-bot-docsgpt-extenstion or 
https://github.com/arc53/DocsGPT-cli

Non-Code Contributions:

📚 Wiki: Improve our documentation, create a guide.

🖥️ Design: Improve the UI/UX or design a new feature.
```

### 📝 Guidelines for Pull Requests:
- Familiarize yourself with the current contributions and our [Roadmap](https://github.com/orgs/arc53/projects/2).
- Before contributing check existing [issues](https://github.com/arc53/DocsGPT/issues) or [create](https://github.com/arc53/DocsGPT/issues/new/choose) an issue and wait to get assigned.
- Once you are finished with your contribution, please fill in this [form](https://forms.gle/Npaba4n9Epfyx56S8).
- Refer to the [Documentation](https://docs.docsgpt.cloud/).
- Feel free to join our [Discord](https://discord.gg/vN7YFfdMpj) server. We're here to help newcomers, so don't hesitate to jump in! Join us [here](https://discord.gg/vN7YFfdMpj).
  
Thank you very much for considering contributing to DocsGPT during Hacktoberfest! 🙏 Your contributions (not just simple typos) could earn you a stylish new t-shirt.

We will publish a t-shirt design later into the October.


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2023 arc53

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<h1 align="center">
  DocsGPT  🦖
</h1>

<p align="center">
  <strong>Private AI for agents, assistants and enterprise search</strong>
</p>

<p align="left">
  <strong><a href="https://www.docsgpt.cloud/">DocsGPT</a></strong> is an open-source AI platform for building intelligent agents and assistants. Features Agent Builder, deep research tools, document analysis (PDF, Office, web content, and audio), Multi-model support (choose your provider or run locally), and rich API connectivity for agents with actionable tools and integrations. Deploy anywhere with complete privacy control.
</p>

<div align="center">
  
  <a href="https://github.com/arc53/DocsGPT">![link to main GitHub showing Stars number](https://img.shields.io/github/stars/arc53/docsgpt?style=social)</a>
  <a href="https://github.com/arc53/DocsGPT">![link to main GitHub showing Forks number](https://img.shields.io/github/forks/arc53/docsgpt?style=social)</a>
  <a href="https://github.com/arc53/DocsGPT/blob/main/LICENSE">![link to license file](https://img.shields.io/github/license/arc53/docsgpt)</a>
  <a href="https://www.bestpractices.dev/projects/9907"><img src="https://www.bestpractices.dev/projects/9907/badge"></a>
  <a href="https://discord.gg/vN7YFfdMpj">![link to discord](https://img.shields.io/discord/1070046503302877216)</a>
  <a href="https://x.com/docsgptai">![X (formerly Twitter) URL](https://img.shields.io/twitter/follow/docsgptai)</a>

<a href="https://docs.docsgpt.cloud/quickstart">⚡️ Quickstart</a> • <a href="https://app.docsgpt.cloud/">☁️ Cloud Version</a> • <a href="https://discord.gg/vN7YFfdMpj">💬 Discord</a>
<br>
<a href="https://docs.docsgpt.cloud/">📖 Documentation</a> • <a href="https://github.com/arc53/DocsGPT/blob/main/CONTRIBUTING.md">👫 Contribute</a> • <a href="https://blog.docsgpt.cloud/">🗞 Blog</a>
<br>

</div>


<div align="center">
  <br>
<img src="https://d3dg1063dc54p9.cloudfront.net/videos/demov7.gif" alt="video-example-of-docs-gpt" width="800" height="450">
</div>
<h3 align="left">
  <strong>Key Features:</strong>
</h3>
<ul align="left">
    <li><strong>🗂️ Wide Format Support:</strong> Reads PDF, DOCX, CSV, XLSX, EPUB, MD, RST, HTML, MDX, JSON, PPTX, images, and audio files such as MP3, WAV, M4A, OGG, and WebM.</li>
    <li><strong>🎙️ Speech Workflows:</strong> Record voice input into chat, transcribe audio on the backend, and ingest meeting recordings or voice notes as searchable knowledge.</li>
    <li><strong>🌐 Web & Data Integration:</strong> Ingests from URLs, sitemaps, Reddit, GitHub and web crawlers.</li>
    <li><strong>✅ Reliable Answers:</strong> Get accurate, hallucination-free responses with source citations viewable in a clean UI.</li>
    <li><strong>🔑 Streamlined API Keys:</strong>  Generate keys linked to your settings, documents, and models, simplifying chatbot and integration setup.</li>
    <li><strong>🔗 Actionable Tooling:</strong> Connect to APIs, tools, and other services to enable LLM actions.</li>
    <li><strong>🧩 Pre-built Integrations:</strong> Use readily available HTML/React chat widgets, search tools, Discord/Telegram bots, and more.</li>
    <li><strong>🔌 Flexible Deployment:</strong> Works with major LLMs (OpenAI, Google, Anthropic) and local models (Ollama, llama_cpp).</li>
    <li><strong>🏢 Secure & Scalable:</strong> Run privately and securely with Kubernetes support, designed for enterprise-grade reliability.</li>
</ul>

## Roadmap
- [x] Add OAuth 2.0 authentication for MCP ( September 2025 )
- [x] Deep Agents ( October 2025 )
- [x] Prompt Templating ( October 2025 )
- [x] Full api tooling ( Dec 2025 )
- [ ] Agent scheduling ( Jan 2026 )

You can find our full roadmap [here](https://github.com/orgs/arc53/projects/2). Please don't hesitate to contribute or create issues, it helps us improve DocsGPT!

### Production Support / Help for Companies:

We're eager to provide personalized assistance when deploying your DocsGPT to a live environment.

[Get a Demo :wave:](https://www.docsgpt.cloud/contact)⁠

[Send Email :email:](mailto:support@docsgpt.cloud?subject=DocsGPT%20support%2Fsolutions)

## Join the Lighthouse Program 🌟

Calling all developers and GenAI innovators! The **DocsGPT Lighthouse Program** connects technical leaders actively deploying or extending DocsGPT in real-world scenarios. Collaborate directly with our team to shape the roadmap, access priority support, and build enterprise-ready solutions with exclusive community insights.

[Learn More & Apply →](https://docs.google.com/forms/d/1KAADiJinUJ8EMQyfTXUIGyFbqINNClNR3jBNWq7DgTE)

## QuickStart

> [!Note]
> Make sure you have [Docker](https://docs.docker.com/engine/install/) installed

A more detailed [Quickstart](https://docs.docsgpt.cloud/quickstart) is available in our documentation

1. **Clone the repository:**

   ```bash
   git clone https://github.com/arc53/DocsGPT.git
   cd DocsGPT
   ```

**For macOS and Linux:**

2. **Run the setup script:**

   ```bash
   ./setup.sh
   ```

**For Windows:**

2. **Run the PowerShell setup script:**

   ```powershell
   PowerShell -ExecutionPolicy Bypass -File .\setup.ps1
   ```

Either script will guide you through setting up DocsGPT. Five options available: using the public API, running locally, connecting to a local inference engine, using a cloud API provider, or build the docker image locally. Scripts will automatically configure your `.env` file and handle necessary downloads and installations based on your chosen option.

**Navigate to http://localhost:5173/**

To stop DocsGPT, open a terminal in the `DocsGPT` directory and run:

```bash
docker compose -f deployment/docker-compose.yaml down
```

(or use the specific `docker compose down` command shown after running the setup script).

> [!Note]
> For development environment setup instructions, please refer to the [Development Environment Guide](https://docs.docsgpt.cloud/Deploying/Development-Environment).

## Contributing

Please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file for information about how to get involved. We welcome issues, questions, and pull requests.

## Architecture

![Architecture chart](https://github.com/user-attachments/assets/fc6a7841-ddfc-45e6-b5a0-d05fe648cbe2)

## Project Structure

- Application - Flask app (main application).

- Extensions - Extensions, like react widget or discord bot.

- Frontend - Frontend uses <a href="https://vitejs.dev/">Vite</a> and <a href="https://react.dev/">React</a>.

- Scripts - Miscellaneous scripts.

## Code Of Conduct

We as members, contributors, and leaders, pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. Please refer to the [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) file for more information about contributing.

## Many Thanks To Our Contributors⚡

<a href="https://github.com/arc53/DocsGPT/graphs/contributors" alt="View Contributors">
  <img src="https://contrib.rocks/image?repo=arc53/DocsGPT" alt="Contributors" />
</a>

## License

The source code license is [MIT](https://opensource.org/license/mit/), as described in the [LICENSE](LICENSE) file.

## This project is supported by:

<p>
  <a href="https://www.digitalocean.com/?utm_medium=opensource&utm_source=DocsGPT">
    <img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
  </a>
</p>
<p>
  <a href="https://get.neon.com/docsgpt">
    <img width="201" alt="color" src="https://github.com/user-attachments/assets/7d9813b7-0e6d-403f-b5af-68af066b326f" />
  </a>
  
</p>


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Supported Versions:

Currently, we support security patches by committing changes and bumping the version published on Github.

## Reporting a Vulnerability

Found a vulnerability? Please email us:

security@arc53.com



================================================
FILE: application/Dockerfile
================================================
# Builder Stage
FROM ubuntu:24.04 as builder

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && \
    apt-get install -y software-properties-common && \
    add-apt-repository ppa:deadsnakes/ppa && \
    apt-get update && \
    apt-get install -y --no-install-recommends gcc g++ wget unzip libc6-dev python3.12 python3.12-venv python3.12-dev && \
    rm -rf /var/lib/apt/lists/* 

# Verify Python installation and setup symlink
RUN if [ -f /usr/bin/python3.12 ]; then \
        ln -s /usr/bin/python3.12 /usr/bin/python; \
    else \
        echo "Python 3.12 not found"; exit 1; \
    fi

# Download and unzip the model
RUN wget https://d3dg1063dc54p9.cloudfront.net/models/embeddings/mpnet-base-v2.zip && \
    unzip mpnet-base-v2.zip -d models && \
    rm mpnet-base-v2.zip

# Install Rust
RUN wget -q -O - https://sh.rustup.rs | sh -s -- -y

# Clean up to reduce container size
RUN apt-get remove --purge -y wget unzip && apt-get autoremove -y && rm -rf /var/lib/apt/lists/*

# Copy requirements.txt
COPY requirements.txt .

# Setup Python virtual environment
RUN python3.12 -m venv /venv

# Activate virtual environment and install Python packages
ENV PATH="/venv/bin:$PATH"

# Install Python packages
RUN pip install --no-cache-dir --upgrade pip && \
    pip install --no-cache-dir tiktoken && \
    pip install --no-cache-dir -r requirements.txt

# Final Stage
FROM ubuntu:24.04 as final

RUN apt-get update && \
    apt-get install -y software-properties-common && \
    add-apt-repository ppa:deadsnakes/ppa && \
    apt-get update && apt-get install -y --no-install-recommends \
        python3.12 \
        libgl1 \
        libglib2.0-0 \
        poppler-utils \
        && \
    ln -s /usr/bin/python3.12 /usr/bin/python && \
    rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /app

# Create a non-root user: `appuser` (Feel free to choose a name)
RUN groupadd -r appuser && \
    useradd -r -g appuser -d /app -s /sbin/nologin -c "Docker image user" appuser

# Copy the virtual environment and model from the builder stage
COPY --from=builder /venv /venv

COPY --from=builder /models /app/models

# Copy your application code
COPY . /app/application

# Change the ownership of the /app directory to the appuser
	
RUN mkdir -p /app/application/inputs/local
RUN chown -R appuser:appuser /app

# Set environment variables
ENV FLASK_APP=app.py \
    FLASK_DEBUG=true \
    PATH="/venv/bin:$PATH"

# Expose the port the app runs on
EXPOSE 7091

# Switch to non-root user
USER appuser

# Start Gunicorn
CMD ["gunicorn", "-w", "1", "--timeout", "120", "--bind", "0.0.0.0:7091", "--preload", "application.wsgi:app"]


================================================
FILE: application/__init__.py
================================================


================================================
FILE: application/agents/__init__.py
================================================


================================================
FILE: application/agents/agent_creator.py
================================================
import logging

from application.agents.classic_agent import ClassicAgent
from application.agents.react_agent import ReActAgent
from application.agents.workflow_agent import WorkflowAgent

logger = logging.getLogger(__name__)


class AgentCreator:
    agents = {
        "classic": ClassicAgent,
        "react": ReActAgent,
        "workflow": WorkflowAgent,
    }

    @classmethod
    def create_agent(cls, type, *args, **kwargs):
        agent_class = cls.agents.get(type.lower())
        if not agent_class:
            raise ValueError(f"No agent class found for type {type}")
        return agent_class(*args, **kwargs)


================================================
FILE: application/agents/base.py
================================================
import logging
import uuid
from abc import ABC, abstractmethod
from typing import Dict, Generator, List, Optional

from bson.objectid import ObjectId

from application.agents.tools.tool_action_parser import ToolActionParser
from application.agents.tools.tool_manager import ToolManager
from application.core.json_schema_utils import (
    JsonSchemaValidationError,
    normalize_json_schema_payload,
)
from application.core.mongo_db import MongoDB
from application.core.settings import settings
from application.llm.handlers.handler_creator import LLMHandlerCreator
from application.llm.llm_creator import LLMCreator
from application.logging import build_stack_data, log_activity, LogContext
from application.security.encryption import decrypt_credentials

logger = logging.getLogger(__name__)


class BaseAgent(ABC):
    def __init__(
        self,
        endpoint: str,
        llm_name: str,
        model_id: str,
        api_key: str,
        agent_id: Optional[str] = None,
        user_api_key: Optional[str] = None,
        prompt: str = "",
        chat_history: Optional[List[Dict]] = None,
        retrieved_docs: Optional[List[Dict]] = None,
        decoded_token: Optional[Dict] = None,
        attachments: Optional[List[Dict]] = None,
        json_schema: Optional[Dict] = None,
        limited_token_mode: Optional[bool] = False,
        token_limit: Optional[int] = settings.DEFAULT_AGENT_LIMITS["token_limit"],
        limited_request_mode: Optional[bool] = False,
        request_limit: Optional[int] = settings.DEFAULT_AGENT_LIMITS["request_limit"],
        compressed_summary: Optional[str] = None,
    ):
        self.endpoint = endpoint
        self.llm_name = llm_name
        self.model_id = model_id
        self.api_key = api_key
        self.agent_id = agent_id
        self.user_api_key = user_api_key
        self.prompt = prompt
        self.decoded_token = decoded_token or {}
        self.user: str = self.decoded_token.get("sub")
        self.tool_config: Dict = {}
        self.tools: List[Dict] = []
        self.tool_calls: List[Dict] = []
        self.chat_history: List[Dict] = chat_history if chat_history is not None else []
        self.llm = LLMCreator.create_llm(
            llm_name,
            api_key=api_key,
            user_api_key=user_api_key,
            decoded_token=decoded_token,
            model_id=model_id,
            agent_id=agent_id,
        )
        self.retrieved_docs = retrieved_docs or []
        self.llm_handler = LLMHandlerCreator.create_handler(
            llm_name if llm_name else "default"
        )
        self.attachments = attachments or []
        self.json_schema = None
        if json_schema is not None:
            try:
                self.json_schema = normalize_json_schema_payload(json_schema)
            except JsonSchemaValidationError as exc:
                logger.warning("Ignoring invalid JSON schema payload: %s", exc)
        self.limited_token_mode = limited_token_mode
        self.token_limit = token_limit
        self.limited_request_mode = limited_request_mode
        self.request_limit = request_limit
        self.compressed_summary = compressed_summary
        self.current_token_count = 0
        self.context_limit_reached = False

    @log_activity()
    def gen(
        self, query: str, log_context: LogContext = None
    ) -> Generator[Dict, None, None]:
        yield from self._gen_inner(query, log_context)

    @abstractmethod
    def _gen_inner(
        self, query: str, log_context: LogContext
    ) -> Generator[Dict, None, None]:
        pass

    def _get_tools(self, api_key: str = None) -> Dict[str, Dict]:
        mongo = MongoDB.get_client()
        db = mongo[settings.MONGO_DB_NAME]
        agents_collection = db["agents"]
        tools_collection = db["user_tools"]

        agent_data = agents_collection.find_one({"key": api_key or self.user_api_key})
        tool_ids = agent_data.get("tools", []) if agent_data else []

        tools = (
            tools_collection.find(
                {"_id": {"$in": [ObjectId(tool_id) for tool_id in tool_ids]}}
            )
            if tool_ids
            else []
        )
        tools = list(tools)
        tools_by_id = {str(tool["_id"]): tool for tool in tools} if tools else {}

        return tools_by_id

    def _get_user_tools(self, user="local"):
        mongo = MongoDB.get_client()
        db = mongo[settings.MONGO_DB_NAME]
        user_tools_collection = db["user_tools"]
        user_tools = user_tools_collection.find({"user": user, "status": True})
        user_tools = list(user_tools)

        return {str(i): tool for i, tool in enumerate(user_tools)}

    def _build_tool_parameters(self, action):
        params = {"type": "object", "properties": {}, "required": []}
        for param_type in ["query_params", "headers", "body", "parameters"]:
            if param_type in action and action[param_type].get("properties"):
                for k, v in action[param_type]["properties"].items():
                    if v.get("filled_by_llm", True):
                        params["properties"][k] = {
                            key: value
                            for key, value in v.items()
                            if key not in ("filled_by_llm", "value", "required")
                        }
                        if v.get("required", False):
                            params["required"].append(k)
        return params

    def _prepare_tools(self, tools_dict):
        self.tools = [
            {
                "type": "function",
                "function": {
                    "name": f"{action['name']}_{tool_id}",
                    "description": action["description"],
                    "parameters": self._build_tool_parameters(action),
                },
            }
            for tool_id, tool in tools_dict.items()
            if (
                (tool["name"] == "api_tool" and "actions" in tool.get("config", {}))
                or (tool["name"] != "api_tool" and "actions" in tool)
            )
            for action in (
                tool["config"]["actions"].values()
                if tool["name"] == "api_tool"
                else tool["actions"]
            )
            if action.get("active", True)
        ]

    def _execute_tool_action(self, tools_dict, call):
        parser = ToolActionParser(self.llm.__class__.__name__)
        tool_id, action_name, call_args = parser.parse_args(call)

        call_id = getattr(call, "id", None) or str(uuid.uuid4())

        # Check if parsing failed

        if tool_id is None or action_name is None:
            error_message = f"Error: Failed to parse LLM tool call. Tool name: {getattr(call, 'name', 'unknown')}"
            logger.error(error_message)

            tool_call_data = {
                "tool_name": "unknown",
                "call_id": call_id,
                "action_name": getattr(call, "name", "unknown"),
                "arguments": call_args or {},
                "result": f"Failed to parse tool call. Invalid tool name format: {getattr(call, 'name', 'unknown')}",
            }
            yield {"type": "tool_call", "data": {**tool_call_data, "status": "error"}}
            self.tool_calls.append(tool_call_data)
            return "Failed to parse tool call.", call_id
        # Check if tool_id exists in available tools

        if tool_id not in tools_dict:
            error_message = f"Error: Tool ID '{tool_id}' extracted from LLM call not found in available tools_dict. Available IDs: {list(tools_dict.keys())}"
            logger.error(error_message)

            # Return error result

            tool_call_data = {
                "tool_name": "unknown",
                "call_id": call_id,
                "action_name": f"{action_name}_{tool_id}",
                "arguments": call_args,
                "result": f"Tool with ID {tool_id} not found. Available tools: {list(tools_dict.keys())}",
            }
            yield {"type": "tool_call", "data": {**tool_call_data, "status": "error"}}
            self.tool_calls.append(tool_call_data)
            return f"Tool with ID {tool_id} not found.", call_id
        tool_call_data = {
            "tool_name": tools_dict[tool_id]["name"],
            "call_id": call_id,
            "action_name": f"{action_name}_{tool_id}",
            "arguments": call_args,
        }
        yield {"type": "tool_call", "data": {**tool_call_data, "status": "pending"}}

        tool_data = tools_dict[tool_id]
        action_data = (
            tool_data["config"]["actions"][action_name]
            if tool_data["name"] == "api_tool"
            else next(
                action
                for action in tool_data["actions"]
                if action["name"] == action_name
            )
        )

        query_params, headers, body, parameters = {}, {}, {}, {}
        param_types = {
            "query_params": query_params,
            "headers": headers,
            "body": body,
            "parameters": parameters,
        }

        for param_type, target_dict in param_types.items():
            if param_type in action_data and action_data[param_type].get("properties"):
                for param, details in action_data[param_type]["properties"].items():
                    if (
                        param not in call_args
                        and "value" in details
                        and details["value"]
                    ):
                        target_dict[param] = details["value"]
        for param, value in call_args.items():
            for param_type, target_dict in param_types.items():
                if param_type in action_data and param in action_data[param_type].get(
                    "properties", {}
                ):
                    target_dict[param] = value
        tm = ToolManager(config={})

        # Prepare tool_config and add tool_id for memory tools

        if tool_data["name"] == "api_tool":
            action_config = tool_data["config"]["actions"][action_name]
            tool_config = {
                "url": action_config["url"],
                "method": action_config["method"],
                "headers": headers,
                "query_params": query_params,
            }
            if "body_content_type" in action_config:
                tool_config["body_content_type"] = action_config.get(
                    "body_content_type", "application/json"
                )
                tool_config["body_encoding_rules"] = action_config.get(
                    "body_encoding_rules", {}
                )
        else:
            tool_config = tool_data["config"].copy() if tool_data["config"] else {}
            if tool_config.get("encrypted_credentials") and self.user:
                decrypted = decrypt_credentials(
                    tool_config["encrypted_credentials"], self.user
                )
                tool_config.update(decrypted)
                tool_config["auth_credentials"] = decrypted
                tool_config.pop("encrypted_credentials", None)
            tool_config["tool_id"] = str(tool_data.get("_id", tool_id))
            if hasattr(self, "conversation_id") and self.conversation_id:
                tool_config["conversation_id"] = self.conversation_id
            if tool_data["name"] == "mcp_tool":
                tool_config["query_mode"] = True
        tool = tm.load_tool(
            tool_data["name"],
            tool_config=tool_config,
            user_id=self.user,
        )
        resolved_arguments = (
            {"query_params": query_params, "headers": headers, "body": body}
            if tool_data["name"] == "api_tool"
            else parameters
        )
        if tool_data["name"] == "api_tool":
            logger.debug(
                f"Executing api: {action_name} with query_params: {query_params}, headers: {headers}, body: {body}"
            )
            result = tool.execute_action(action_name, **body)
        else:
            logger.debug(f"Executing tool: {action_name} with args: {call_args}")
            result = tool.execute_action(action_name, **parameters)

        get_artifact_id = (
            getattr(tool, "get_artifact_id", None)
            if tool_data["name"] != "api_tool"
            else None
        )

        artifact_id = None
        if callable(get_artifact_id):
            try:
                artifact_id = get_artifact_id(action_name, **parameters)
            except Exception:
                logger.exception(
                    "Failed to extract artifact_id from tool %s for action %s",
                    tool_data["name"],
                    action_name,
                )

        artifact_id = str(artifact_id).strip() if artifact_id is not None else ""
        if artifact_id:
            tool_call_data["artifact_id"] = artifact_id
        result_full = str(result)
        tool_call_data["resolved_arguments"] = resolved_arguments
        tool_call_data["result_full"] = result_full
        tool_call_data["result"] = (
            f"{result_full[:50]}..." if len(result_full) > 50 else result_full
        )

        stream_tool_call_data = {
            key: value
            for key, value in tool_call_data.items()
            if key not in {"result_full", "resolved_arguments"}
        }
        yield {"type": "tool_call", "data": {**stream_tool_call_data, "status": "completed"}}
        self.tool_calls.append(tool_call_data)

        return result, call_id

    def _get_truncated_tool_calls(self):
        return [
            {
                "tool_name": tool_call.get("tool_name"),
                "call_id": tool_call.get("call_id"),
                "action_name": tool_call.get("action_name"),
                "arguments": tool_call.get("arguments"),
                "artifact_id": tool_call.get("artifact_id"),
                "result": (
                    f"{str(tool_call['result'])[:50]}..."
                    if len(str(tool_call["result"])) > 50
                    else tool_call["result"]
                ),
                "status": "completed",
            }
            for tool_call in self.tool_calls
        ]

    def _calculate_current_context_tokens(self, messages: List[Dict]) -> int:
        """
        Calculate total tokens in current context (messages).

        Args:
            messages: List of message dicts

        Returns:
            Total token count
        """
        from application.api.answer.services.compression.token_counter import (
            TokenCounter,
        )

        return TokenCounter.count_message_tokens(messages)

    def _check_context_limit(self, messages: List[Dict]) -> bool:
        """
        Check if we're approaching context limit (80%).

        Args:
            messages: Current message list

        Returns:
            True if at or above 80% of context limit
        """
        from application.core.model_utils import get_token_limit
        from application.core.settings import settings

        try:
            # Calculate current tokens
            current_tokens = self._calculate_current_context_tokens(messages)
            self.current_token_count = current_tokens

            # Get context limit for model
            context_limit = get_token_limit(self.model_id)

            # Calculate threshold (80%)
            threshold = int(context_limit * settings.COMPRESSION_THRESHOLD_PERCENTAGE)

            # Check if we've reached the limit
            if current_tokens >= threshold:
                logger.warning(
                    f"Context limit approaching: {current_tokens}/{context_limit} tokens "
                    f"({(current_tokens/context_limit)*100:.1f}%)"
                )
                return True

            return False

        except Exception as e:
            logger.error(f"Error checking context limit: {str(e)}", exc_info=True)
            return False

    def _validate_context_size(self, messages: List[Dict]) -> None:
        """
        Pre-flight validation before calling LLM. Logs warnings but never raises errors.

        Args:
            messages: Messages to be sent to LLM
        """
        from application.core.model_utils import get_token_limit

        current_tokens = self._calculate_current_context_tokens(messages)
        self.current_token_count = current_tokens
        context_limit = get_token_limit(self.model_id)

        percentage = (current_tokens / context_limit) * 100

        # Log based on usage level
        if current_tokens >= context_limit:
            logger.warning(
                f"Context at limit: {current_tokens:,}/{context_limit:,} tokens "
                f"({percentage:.1f}%). Model: {self.model_id}"
            )
        elif current_tokens >= int(
            context_limit * settings.COMPRESSION_THRESHOLD_PERCENTAGE
        ):
            logger.info(
                f"Context approaching limit: {current_tokens:,}/{context_limit:,} tokens "
                f"({percentage:.1f}%)"
            )

    def _truncate_text_middle(self, text: str, max_tokens: int) -> str:
        """
        Truncate text by removing content from the middle, preserving start and end.

        Args:
            text: Text to truncate
            max_tokens: Maximum tokens allowed

        Returns:
            Truncated text with middle removed if needed
        """
        from application.utils import num_tokens_from_string

        current_tokens = num_tokens_from_string(text)
        if current_tokens <= max_tokens:
            return text

        # Estimate chars per token (roughly 4 chars per token for English)
        chars_per_token = len(text) / current_tokens if current_tokens > 0 else 4
        target_chars = int(max_tokens * chars_per_token * 0.95)  # 5% safety margin

        if target_chars <= 0:
            return ""

        # Split: keep 40% from start, 40% from end, remove middle
        start_chars = int(target_chars * 0.4)
        end_chars = int(target_chars * 0.4)

        truncation_marker = "\n\n[... content truncated to fit context limit ...]\n\n"

        truncated = text[:start_chars] + truncation_marker + text[-end_chars:]

        logger.info(
            f"Truncated text from {current_tokens:,} to ~{max_tokens:,} tokens "
            f"(removed middle section)"
        )

        return truncated

    def _build_messages(
        self,
        system_prompt: str,
        query: str,
    ) -> List[Dict]:
        """Build messages using pre-rendered system prompt"""
        from application.core.model_utils import get_token_limit
        from application.utils import num_tokens_from_string

        # Append compression summary to system prompt if present
        if self.compressed_summary:
            compression_context = (
                "\n\n---\n\n"
                "This session is being continued from a previous conversation that "
                "has been compressed to fit within context limits. "
                "The conversation is summarized below:\n\n"
                f"{self.compressed_summary}"
            )
            system_prompt = system_prompt + compression_context

        context_limit = get_token_limit(self.model_id)
        system_tokens = num_tokens_from_string(system_prompt)

        # Reserve 10% for response/tools
        safety_buffer = int(context_limit * 0.1)
        available_after_system = context_limit - system_tokens - safety_buffer

        # Max tokens for query: 80% of available space (leave room for history)
        max_query_tokens = int(available_after_system * 0.8)
        query_tokens = num_tokens_from_string(query)

        # Truncate query from middle if it exceeds 80% of available context
        if query_tokens > max_query_tokens:
            query = self._truncate_text_middle(query, max_query_tokens)
            query_tokens = num_tokens_from_string(query)

        # Calculate remaining budget for chat history
        available_for_history = max(available_after_system - query_tokens, 0)

        # Truncate chat history to fit within available budget
        working_history = self._truncate_history_to_fit(
            self.chat_history,
            available_for_history,
        )

        messages = [{"role": "system", "content": system_prompt}]

        for i in working_history:
            if "prompt" in i and "response" in i:
                messages.append({"role": "user", "content": i["prompt"]})
                messages.append({"role": "assistant", "content": i["response"]})
            if "tool_calls" in i:
                for tool_call in i["tool_calls"]:
                    call_id = tool_call.get("call_id") or str(uuid.uuid4())

                    function_call_dict = {
                        "function_call": {
                            "name": tool_call.get("action_name"),
                            "args": tool_call.get("arguments"),
                            "call_id": call_id,
                        }
                    }
                    function_response_dict = {
                        "function_response": {
                            "name": tool_call.get("action_name"),
                            "response": {"result": tool_call.get("result")},
                            "call_id": call_id,
                        }
                    }

                    messages.append(
                        {"role": "assistant", "content": [function_call_dict]}
                    )
                    messages.append(
                        {"role": "tool", "content": [function_response_dict]}
                    )
        messages.append({"role": "user", "content": query})
        return messages

    def _truncate_history_to_fit(
        self,
        history: List[Dict],
        max_tokens: int,
    ) -> List[Dict]:
        """
        Truncate chat history to fit within token budget, keeping most recent messages.

        Args:
            history: Full chat history
            max_tokens: Maximum tokens allowed for history

        Returns:
            Truncated history (most recent messages that fit)
        """
        from application.utils import num_tokens_from_string

        if not history or max_tokens <= 0:
            return []

        truncated = []
        current_tokens = 0

        # Iterate from newest to oldest
        for message in reversed(history):
            message_tokens = 0

            if "prompt" in message and "response" in message:
                message_tokens += num_tokens_from_string(message["prompt"])
                message_tokens += num_tokens_from_string(message["response"])

            if "tool_calls" in message:
                for tool_call in message["tool_calls"]:
                    tool_str = (
                        f"Tool: {tool_call.get('tool_name')} | "
                        f"Action: {tool_call.get('action_name')} | "
                        f"Args: {tool_call.get('arguments')} | "
                        f"Response: {tool_call.get('result')}"
                    )
                    message_tokens += num_tokens_from_string(tool_str)

            if current_tokens + message_tokens <= max_tokens:
                current_tokens += message_tokens
                truncated.insert(0, message)  # Maintain chronological order
            else:
                break

        if len(truncated) < len(history):
            logger.info(
                f"Truncated chat history from {len(history)} to {len(truncated)} messages "
                f"to fit within {max_tokens:,} token budget"
            )

        return truncated

    def _llm_gen(self, messages: List[Dict], log_context: Optional[LogContext] = None):
        # Pre-flight context validation - fail fast if over limit
        self._validate_context_size(messages)

        gen_kwargs = {"model": self.model_id, "messages": messages}
        if self.attachments:
            # Usage accounting only; stripped before provider invocation.
            gen_kwargs["_usage_attachments"] = self.attachments

        if (
            hasattr(self.llm, "_supports_tools")
            and self.llm._supports_tools
            and self.tools
        ):
            gen_kwargs["tools"] = self.tools
        if (
            self.json_schema
            and hasattr(self.llm, "_supports_structured_output")
            and self.llm._supports_structured_output()
        ):
            structured_format = self.llm.prepare_structured_output_format(
                self.json_schema
            )
            if structured_format:
                if self.llm_name == "openai":
                    gen_kwargs["response_format"] = structured_format
                elif self.llm_name == "google":
                    gen_kwargs["response_schema"] = structured_format
        resp = self.llm.gen_stream(**gen_kwargs)

        if log_context:
            data = build_stack_data(self.llm, exclude_attributes=["client"])
            log_context.stacks.append({"component": "llm", "data": data})
        return resp

    def _llm_handler(
        self,
        resp,
        tools_dict: Dict,
        messages: List[Dict],
        log_context: Optional[LogContext] = None,
        attachments: Optional[List[Dict]] = None,
    ):
        resp = self.llm_handler.process_message_flow(
            self, resp, tools_dict, messages, attachments, True
        )
        if log_context:
            data = build_stack_data(self.llm_handler, exclude_attributes=["tool_calls"])
            log_context.stacks.append({"component": "llm_handler", "data": data})
        return resp

    def _handle_response(self, response, tools_dict, messages, log_context):
        is_structured_output = (
            self.json_schema is not None
            and hasattr(self.llm, "_supports_structured_output")
            and self.llm._supports_structured_output()
        )

        if isinstance(response, str):
            answer_data = {"answer": response}
            if is_structured_output:
                answer_data["structured"] = True
                answer_data["schema"] = self.json_schema
            yield answer_data
            return
        if hasattr(response, "message") and getattr(response.message, "content", None):
            answer_data = {"answer": response.message.content}
            if is_structured_output:
                answer_data["structured"] = True
                answer_data["schema"] = self.json_schema
            yield answer_data
            return
        processed_response_gen = self._llm_handler(
            response, tools_dict, messages, log_context, self.attachments
        )

        for event in processed_response_gen:
            if isinstance(event, str):
                answer_data = {"answer": event}
                if is_structured_output:
                    answer_data["structured"] = True
                    answer_data["schema"] = self.json_schema
                yield answer_data
            elif hasattr(event, "message") and getattr(event.message, "content", None):
                answer_data = {"answer": event.message.content}
                if is_structured_output:
                    answer_data["structured"] = True
                    answer_data["schema"] = self.json_schema
                yield answer_data
            elif isinstance(event, dict) and "type" in event:
                yield event


================================================
FILE: application/agents/classic_agent.py
================================================
import logging
from typing import Dict, Generator

from application.agents.base import BaseAgent
from application.logging import LogContext

logger = logging.getLogger(__name__)


class ClassicAgent(BaseAgent):
    """A simplified agent with clear execution flow"""

    def _gen_inner(
        self, query: str, log_context: LogContext
    ) -> Generator[Dict, None, None]:
        """Core generator function for ClassicAgent execution flow"""

        tools_dict = (
            self._get_user_tools(self.user)
            if not self.user_api_key
            else self._get_tools(self.user_api_key)
        )
        self._prepare_tools(tools_dict)

        messages = self._build_messages(self.prompt, query)
        llm_response = self._llm_gen(messages, log_context)

        yield from self._handle_response(
            llm_response, tools_dict, messages, log_context
        )

        yield {"sources": self.retrieved_docs}
        yield {"tool_calls": self._get_truncated_tool_calls()}

        log_context.stacks.append(
            {"component": "agent", "data": {"tool_calls": self.tool_calls.copy()}}
        )


================================================
FILE: application/agents/react_agent.py
================================================
import logging
import os
from typing import Any, Dict, Generator, List

from application.agents.base import BaseAgent
from application.logging import build_stack_data, LogContext

logger = logging.getLogger(__name__)

MAX_ITERATIONS_REASONING = 10

current_dir = os.path.dirname(
    os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
)
with open(
    os.path.join(current_dir, "application/prompts", "react_planning_prompt.txt"), "r"
) as f:
    PLANNING_PROMPT_TEMPLATE = f.read()
with open(
    os.path.join(current_dir, "application/prompts", "react_final_prompt.txt"), "r"
) as f:
    FINAL_PROMPT_TEMPLATE = f.read()


class ReActAgent(BaseAgent):
    """
    Research and Action (ReAct) Agent - Advanced reasoning agent with iterative planning.

    Implements a think-act-observe loop for complex problem-solving:
    1. Creates a strategic plan based on the query
    2. Executes tools and gathers observations
    3. Iteratively refines approach until satisfied
    4. Synthesizes final answer from all observations
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.plan: str = ""
        self.observations: List[str] = []

    def _gen_inner(
        self, query: str, log_context: LogContext
    ) -> Generator[Dict, None, None]:
        """Execute ReAct reasoning loop with planning, action, and observation cycles"""

        self._reset_state()

        tools_dict = (
            self._get_tools(self.user_api_key)
            if self.user_api_key
            else self._get_user_tools(self.user)
        )
        self._prepare_tools(tools_dict)

        for iteration in range(1, MAX_ITERATIONS_REASONING + 1):
            yield {"thought": f"Reasoning... (iteration {iteration})\n\n"}

            yield from self._planning_phase(query, log_context)

            if not self.plan:
                logger.warning(
                    f"ReActAgent: No plan generated in iteration {iteration}"
                )
                break
            self.observations.append(f"Plan (iteration {iteration}): {self.plan}")

            satisfied = yield from self._execution_phase(query, tools_dict, log_context)

            if satisfied:
                logger.info("ReActAgent: Goal satisfied, stopping reasoning loop")
                break
        yield from self._synthesis_phase(query, log_context)

    def _reset_state(self):
        """Reset agent state for new query"""
        self.plan = ""
        self.observations = []

    def _planning_phase(
        self, query: str, log_context: LogContext
    ) -> Generator[Dict, None, None]:
        """Generate strategic plan for query"""
        logger.info("ReActAgent: Creating plan...")

        plan_prompt = self._build_planning_prompt(query)
        messages = [{"role": "user", "content": plan_prompt}]

        plan_stream = self.llm.gen_stream(
            model=self.model_id,
            messages=messages,
            tools=self.tools if self.tools else None,
        )

        if log_context:
            log_context.stacks.append(
                {"component": "planning_llm", "data": build_stack_data(self.llm)}
            )
        plan_parts = []
        for chunk in plan_stream:
            content = self._extract_content(chunk)
            if content:
                plan_parts.append(content)
                yield {"thought": content}
        self.plan = "".join(plan_parts)

    def _execution_phase(
        self, query: str, tools_dict: Dict, log_context: LogContext
    ) -> Generator[bool, None, None]:
        """Execute plan with tool calls and observations"""
        execution_prompt = self._build_execution_prompt(query)
        messages = self._build_messages(execution_prompt, query)

        llm_response = self._llm_gen(messages, log_context)
        initial_content = self._extract_content(llm_response)

        if initial_content:
            self.observations.append(f"Initial response: {initial_content}")
        processed_response = self._llm_handler(
            llm_response, tools_dict, messages, log_context
        )

        for tool_call in self.tool_calls:
            observation = (
                f"Executed: {tool_call.get('tool_name', 'Unknown')} "
                f"with args {tool_call.get('arguments', {})}. "
                f"Result: {str(tool_call.get('result', ''))[:200]}"
            )
            self.observations.append(observation)
        final_content = self._extract_content(processed_response)
        if final_content:
            self.observations.append(f"Response after tools: {final_content}")
        if log_context:
            log_context.stacks.append(
                {
                    "component": "agent_tool_calls",
                    "data": {"tool_calls": self.tool_calls.copy()},
                }
            )
        yield {"sources": self.retrieved_docs}
        yield {"tool_calls": self._get_truncated_tool_calls()}

        return "SATISFIED" in (final_content or "")

    def _synthesis_phase(
        self, query: str, log_context: LogContext
    ) -> Generator[Dict, None, None]:
        """Synthesize final answer from all observations"""
        logger.info("ReActAgent: Generating final answer...")

        final_prompt = self._build_final_answer_prompt(query)
        messages = [{"role": "user", "content": final_prompt}]

        final_stream = self.llm.gen_stream(
            model=self.model_id, messages=messages, tools=None
        )

        if log_context:
            log_context.stacks.append(
                {"component": "final_answer_llm", "data": build_stack_data(self.llm)}
            )
        for chunk in final_stream:
            content = self._extract_content(chunk)
            if content:
                yield {"answer": content}

    def _build_planning_prompt(self, query: str) -> str:
        """Build planning phase prompt"""
        prompt = PLANNING_PROMPT_TEMPLATE.replace("{query}", query)
        prompt = prompt.replace("{prompt}", self.prompt or "")
        prompt = prompt.replace("{summaries}", "")
        prompt = prompt.replace("{observations}", "\n".join(self.observations))
        return prompt

    def _build_execution_prompt(self, query: str) -> str:
        """Build execution phase prompt with plan and observations"""
        observations_str = "\n".join(self.observations)

        if len(observations_str) > 20000:
            observations_str = observations_str[:20000] + "\n...[truncated]"
        return (
            f"{self.prompt or ''}\n\n"
            f"Follow this plan:\n{self.plan}\n\n"
            f"Observations:\n{observations_str}\n\n"
            f"If sufficient data exists to answer '{query}', respond with 'SATISFIED'. "
            f"Otherwise, continue executing the plan."
        )

    def _build_final_answer_prompt(self, query: str) -> str:
        """Build final synthesis prompt"""
        observations_str = "\n".join(self.observations)

        if len(observations_str) > 10000:
            observations_str = observations_str[:10000] + "\n...[truncated]"
            logger.warning("ReActAgent: Observations truncated for final answer")
        return FINAL_PROMPT_TEMPLATE.format(query=query, observations=observations_str)

    def _extract_content(self, response: Any) -> str:
        """Extract text content from various LLM response formats"""
        if not response:
            return ""
        collected = []

        if isinstance(response, str):
            return response
        if hasattr(response, "message") and hasattr(response.message, "content"):
            if response.message.content:
                return response.message.content
        if hasattr(response, "choices") and response.choices:
            if hasattr(response.choices[0], "message"):
                content = response.choices[0].message.content
                if content:
                    return content
        if hasattr(response, "content") and isinstance(response.content, list):
            if response.content and hasattr(response.content[0], "text"):
                return response.content[0].text
        try:
            for chunk in response:
                content_piece = ""

                if hasattr(chunk, "choices") and chunk.choices:
                    if hasattr(chunk.choices[0], "delta"):
                        delta_content = chunk.choices[0].delta.content
                        if delta_content:
                            content_piece = delta_content
                elif hasattr(chunk, "type") and chunk.type == "content_block_delta":
                    if hasattr(chunk, "delta") and hasattr(chunk.delta, "text"):
                        content_piece = chunk.delta.text
                elif isinstance(chunk, str):
                    content_piece = chunk
                if content_piece:
                    collected.append(content_piece)
        except (TypeError, AttributeError):
            logger.debug(
                f"Response not iterable or unexpected format: {type(response)}"
            )
        except Exception as e:
            logger.error(f"Error extracting content: {e}")
        return "".join(collected)

================================================
FILE: application/agents/tools/api_body_serializer.py
================================================
import base64
import json
import logging
from enum import Enum
from typing import Any, Dict, Optional, Union
from urllib.parse import quote, urlencode

logger = logging.getLogger(__name__)


class ContentType(str, Enum):
    """Supported content types for request bodies."""

    JSON = "application/json"
    FORM_URLENCODED = "application/x-www-form-urlencoded"
    MULTIPART_FORM_DATA = "multipart/form-data"
    TEXT_PLAIN = "text/plain"
    XML = "application/xml"
    OCTET_STREAM = "application/octet-stream"


class RequestBodySerializer:
    """Serializes request bodies according to content-type and OpenAPI 3.1 spec."""

    @staticmethod
    def serialize(
        body_data: Dict[str, Any],
        content_type: str = ContentType.JSON,
        encoding_rules: Optional[Dict[str, Dict[str, Any]]] = None,
    ) -> tuple[Union[str, bytes], Dict[str, str]]:
        """
        Serialize body data to appropriate format.

        Args:
            body_data: Dictionary of body parameters
            content_type: Content-Type header value
            encoding_rules: OpenAPI Encoding Object rules per field

        Returns:
            Tuple of (serialized_body, updated_headers_dict)

        Raises:
            ValueError: If serialization fails
        """
        if not body_data:
            return None, {}

        try:
            content_type_lower = content_type.lower().split(";")[0].strip()

            if content_type_lower == ContentType.JSON:
                return RequestBodySerializer._serialize_json(body_data)

            elif content_type_lower == ContentType.FORM_URLENCODED:
                return RequestBodySerializer._serialize_form_urlencoded(
                    body_data, encoding_rules
                )

            elif content_type_lower == ContentType.MULTIPART_FORM_DATA:
                return RequestBodySerializer._serialize_multipart_form_data(
                    body_data, encoding_rules
                )

            elif content_type_lower == ContentType.TEXT_PLAIN:
                return RequestBodySerializer._serialize_text_plain(body_data)

            elif content_type_lower == ContentType.XML:
                return RequestBodySerializer._serialize_xml(body_data)

            elif content_type_lower == ContentType.OCTET_STREAM:
                return RequestBodySerializer._serialize_octet_stream(body_data)

            else:
                logger.warning(
                    f"Unknown content type: {content_type}, treating as JSON"
                )
                return RequestBodySerializer._serialize_json(body_data)

        except Exception as e:
            logger.error(f"Error serializing body: {str(e)}", exc_info=True)
            raise ValueError(f"Failed to serialize request body: {str(e)}")

    @staticmethod
    def _serialize_json(body_data: Dict[str, Any]) -> tuple[str, Dict[str, str]]:
        """Serialize body as JSON per OpenAPI spec."""
        try:
            serialized = json.dumps(
                body_data, separators=(",", ":"), ensure_ascii=False
            )
            headers = {"Content-Type": ContentType.JSON.value}
            return serialized, headers
        except (TypeError, ValueError) as e:
            raise ValueError(f"Failed to serialize JSON body: {str(e)}")

    @staticmethod
    def _serialize_form_urlencoded(
        body_data: Dict[str, Any],
        encoding_rules: Optional[Dict[str, Dict[str, Any]]] = None,
    ) -> tuple[str, Dict[str, str]]:
        """Serialize body as application/x-www-form-urlencoded per RFC1866/RFC3986."""
        encoding_rules = encoding_rules or {}
        params = []

        for key, value in body_data.items():
            if value is None:
                continue

            rule = encoding_rules.get(key, {})
            style = rule.get("style", "form")
            explode = rule.get("explode", style == "form")
            content_type = rule.get("contentType", "text/plain")

            serialized_value = RequestBodySerializer._serialize_form_value(
                value, style, explode, content_type, key
            )

            if isinstance(serialized_value, list):
                for sv in serialized_value:
                    params.append((key, sv))
            else:
                params.append((key, serialized_value))

        # Use standard urlencode (replaces space with +)
        serialized = urlencode(params, safe="")
        headers = {"Content-Type": ContentType.FORM_URLENCODED.value}
        return serialized, headers

    @staticmethod
    def _serialize_form_value(
        value: Any, style: str, explode: bool, content_type: str, key: str
    ) -> Union[str, list]:
        """Serialize individual form value with encoding rules."""
        if isinstance(value, dict):
            if content_type == "application/json":
                return json.dumps(value, separators=(",", ":"))
            elif content_type == "application/xml":
                return RequestBodySerializer._dict_to_xml(value)
            else:
                if style == "deepObject" and explode:
                    return [
                        f"{RequestBodySerializer._percent_encode(str(v))}"
                        for v in value.values()
                    ]
                elif explode:
                    return [
                        f"{RequestBodySerializer._percent_encode(str(v))}"
                        for v in value.values()
                    ]
                else:
                    pairs = [f"{k},{v}" for k, v in value.items()]
                    return RequestBodySerializer._percent_encode(",".join(pairs))

        elif isinstance(value, (list, tuple)):
            if explode:
                return [
                    RequestBodySerializer._percent_encode(str(item)) for item in value
                ]
            else:
                return RequestBodySerializer._percent_encode(
                    ",".join(str(v) for v in value)
                )

        else:
            return RequestBodySerializer._percent_encode(str(value))

    @staticmethod
    def _serialize_multipart_form_data(
        body_data: Dict[str, Any],
        encoding_rules: Optional[Dict[str, Dict[str, Any]]] = None,
    ) -> tuple[bytes, Dict[str, str]]:
        """
        Serialize body as multipart/form-data per RFC7578.

        Supports file uploads and encoding rules.
        """
        import secrets

        encoding_rules = encoding_rules or {}
        boundary = f"----DocsGPT{secrets.token_hex(16)}"
        parts = []

        for key, value in body_data.items():
            if value is None:
                continue

            rule = encoding_rules.get(key, {})
            content_type = rule.get("contentType", "text/plain")
            headers_rule = rule.get("headers", {})

            part = RequestBodySerializer._create_multipart_part(
                key, value, content_type, headers_rule
            )
            parts.append(part)

        body_bytes = f"--{boundary}\r\n".encode("utf-8")
        body_bytes += f"--{boundary}\r\n".join(parts).encode("utf-8")
        body_bytes += f"\r\n--{boundary}--\r\n".encode("utf-8")

        headers = {
            "Content-Type": f"multipart/form-data; boundary={boundary}",
        }
        return body_bytes, headers

    @staticmethod
    def _create_multipart_part(
        name: str, value: Any, content_type: str, headers_rule: Dict[str, Any]
    ) -> str:
        """Create a single multipart/form-data part."""
        headers = [
            f'Content-Disposition: form-data; name="{RequestBodySerializer._percent_encode(name)}"'
        ]

        if isinstance(value, bytes):
            if content_type == "application/octet-stream":
                value_encoded = base64.b64encode(value).decode("utf-8")
            else:
                value_encoded = value.decode("utf-8", errors="replace")
            headers.append(f"Content-Type: {content_type}")
            headers.append("Content-Transfer-Encoding: base64")
        elif isinstance(value, dict):
            if content_type == "application/json":
                value_encoded = json.dumps(value, separators=(",", ":"))
            elif content_type == "application/xml":
                value_encoded = RequestBodySerializer._dict_to_xml(value)
            else:
                value_encoded = str(value)
            headers.append(f"Content-Type: {content_type}")
        elif isinstance(value, str) and content_type != "text/plain":
            try:
                if content_type == "application/json":
                    json.loads(value)
                    value_encoded = value
                elif content_type == "application/xml":
                    value_encoded = value
                else:
                    value_encoded = str(value)
            except json.JSONDecodeError:
                value_encoded = str(value)
            headers.append(f"Content-Type: {content_type}")
        else:
            value_encoded = str(value)
            if content_type != "text/plain":
                headers.append(f"Content-Type: {content_type}")

        part = "\r\n".join(headers) + "\r\n\r\n" + value_encoded + "\r\n"
        return part

    @staticmethod
    def _serialize_text_plain(body_data: Dict[str, Any]) -> tuple[str, Dict[str, str]]:
        """Serialize body as plain text."""
        if len(body_data) == 1:
            value = list(body_data.values())[0]
            return str(value), {"Content-Type": ContentType.TEXT_PLAIN.value}
        else:
            text = "\n".join(f"{k}: {v}" for k, v in body_data.items())
            return text, {"Content-Type": ContentType.TEXT_PLAIN.value}

    @staticmethod
    def _serialize_xml(body_data: Dict[str, Any]) -> tuple[str, Dict[str, str]]:
        """Serialize body as XML."""
        xml_str = RequestBodySerializer._dict_to_xml(body_data)
        return xml_str, {"Content-Type": ContentType.XML.value}

    @staticmethod
    def _serialize_octet_stream(
        body_data: Dict[str, Any],
    ) -> tuple[bytes, Dict[str, str]]:
        """Serialize body as binary octet stream."""
        if isinstance(body_data, bytes):
            return body_data, {"Content-Type": ContentType.OCTET_STREAM.value}
        elif isinstance(body_data, str):
            return body_data.encode("utf-8"), {
                "Content-Type": ContentType.OCTET_STREAM.value
            }
        else:
            serialized = json.dumps(body_data)
            return serialized.encode("utf-8"), {
                "Content-Type": ContentType.OCTET_STREAM.value
            }

    @staticmethod
    def _percent_encode(value: str, safe_chars: str = "") -> str:
        """
        Percent-encode per RFC3986.

        Args:
            value: String to encode
            safe_chars: Additional characters to not encode
        """
        return quote(value, safe=safe_chars)

    @staticmethod
    def _dict_to_xml(data: Dict[str, Any], root_name: str = "root") -> str:
        """
        Convert dict to simple XML format.
        """

        def build_xml(obj: Any, name: str) -> str:
            if isinstance(obj, dict):
                inner = "".join(build_xml(v, k) for k, v in obj.items())
                return f"<{name}>{inner}</{name}>"
            elif isinstance(obj, (list, tuple)):
                items = "".join(
                    build_xml(item, f"{name[:-1] if name.endswith('s') else name}")
                    for item in obj
                )
                return items
            else:
                return f"<{name}>{RequestBodySerializer._escape_xml(str(obj))}</{name}>"

        root = build_xml(data, root_name)
        return f'<?xml version="1.0" encoding="UTF-8"?>{root}'

    @staticmethod
    def _escape_xml(value: str) -> str:
        """Escape XML special characters."""
        return (
            value.replace("&", "&amp;")
            .replace("<", "&lt;")
            .replace(">", "&gt;")
            .replace('"', "&quot;")
            .replace("'", "&apos;")
        )


================================================
FILE: application/agents/tools/api_tool.py
================================================
import json
import logging
import re
from typing import Any, Dict, Optional
from urllib.parse import urlencode

import requests

from application.agents.tools.api_body_serializer import (
    ContentType,
    RequestBodySerializer,
)
from application.agents.tools.base import Tool
from application.core.url_validation import validate_url, SSRFError

logger = logging.getLogger(__name__)

DEFAULT_TIMEOUT = 90  # seconds


class APITool(Tool):
    """
    API Tool
    A flexible tool for performing various API actions (e.g., sending messages, retrieving data) via custom user-specified APIs.
    """

    def __init__(self, config):
        self.config = config
        self.url = config.get("url", "")
        self.method = config.get("method", "GET")
        self.headers = config.get("headers", {})
        self.query_params = config.get("query_params", {})
        self.body_content_type = config.get("body_content_type", ContentType.JSON)
        self.body_encoding_rules = config.get("body_encoding_rules", {})

    def execute_action(self, action_name, **kwargs):
        """Execute an API action with the given arguments."""
        return self._make_api_call(
            self.url,
            self.method,
            self.headers,
            self.query_params,
            kwargs,
            self.body_content_type,
            self.body_encoding_rules,
        )

    def _make_api_call(
        self,
        url: str,
        method: str,
        headers: Dict[str, str],
        query_params: Dict[str, Any],
        body: Dict[str, Any],
        content_type: str = ContentType.JSON,
        encoding_rules: Optional[Dict[str, Dict[str, Any]]] = None,
    ) -> Dict[str, Any]:
        """
        Make an API call with proper body serialization and error handling.

        Args:
            url: API endpoint URL
            method: HTTP method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
            headers: Request headers dict
            query_params: URL query parameters
            body: Request body as dict
            content_type: Content-Type for serialization
            encoding_rules: OpenAPI encoding rules

        Returns:
            Dict with status_code, data, and message
        """
        request_url = url
        request_headers = headers.copy() if headers else {}
        response = None

        # Validate URL to prevent SSRF attacks
        try:
            validate_url(request_url)
        except SSRFError as e:
            logger.error(f"URL validation failed: {e}")
            return {
                "status_code": None,
                "message": f"URL validation error: {e}",
                "data": None,
            }

        try:
            path_params_used = set()
            if query_params:
                for match in re.finditer(r"\{([^}]+)\}", request_url):
                    param_name = match.group(1)
                    if param_name in query_params:
                        request_url = request_url.replace(
                            f"{{{param_name}}}", str(query_params[param_name])
                        )
                        path_params_used.add(param_name)
            remaining_params = {
                k: v for k, v in query_params.items() if k not in path_params_used
            }
            if remaining_params:
                query_string = urlencode(remaining_params)
                separator = "&" if "?" in request_url else "?"
                request_url = f"{request_url}{separator}{query_string}"

            # Re-validate URL after parameter substitution to prevent SSRF via path params
            try:
                validate_url(request_url)
            except SSRFError as e:
                logger.error(f"URL validation failed after parameter substitution: {e}")
                return {
                    "status_code": None,
                    "message": f"URL validation error: {e}",
                    "data": None,
                }

            # Serialize body based on content type

            if body and body != {}:
                try:
                    serialized_body, body_headers = RequestBodySerializer.serialize(
                        body, content_type, encoding_rules
                    )
                    request_headers.update(body_headers)
                except ValueError as e:
                    logger.error(f"Body serialization failed: {str(e)}")
                    return {
                        "status_code": None,
                        "message": f"Body serialization error: {str(e)}",
                        "data": None,
                    }
            else:
                serialized_body = None
            if "Content-Type" not in request_headers and method not in [
                "GET",
                "HEAD",
                "DELETE",
            ]:
                request_headers["Content-Type"] = ContentType.JSON
            logger.debug(
                f"API Call: {method} {request_url} | Content-Type: {request_headers.get('Content-Type', 'N/A')}"
            )

            if method.upper() == "GET":
                response = requests.get(
                    request_url, headers=request_headers, timeout=DEFAULT_TIMEOUT
                )
            elif method.upper() == "POST":
                response = requests.post(
                    request_url,
                    data=serialized_body,
                    headers=request_headers,
                    timeout=DEFAULT_TIMEOUT,
                )
            elif method.upper() == "PUT":
                response = requests.put(
                    request_url,
                    data=serialized_body,
                    headers=request_headers,
                    timeout=DEFAULT_TIMEOUT,
                )
            elif method.upper() == "DELETE":
                response = requests.delete(
                    request_url, headers=request_headers, timeout=DEFAULT_TIMEOUT
                )
            elif method.upper() == "PATCH":
                response = requests.patch(
                    request_url,
                    data=serialized_body,
                    headers=request_headers,
                    timeout=DEFAULT_TIMEOUT,
                )
            elif method.upper() == "HEAD":
                response = requests.head(
                    request_url, headers=request_headers, timeout=DEFAULT_TIMEOUT
                )
            elif method.upper() == "OPTIONS":
                response = requests.options(
                    request_url, headers=request_headers, timeout=DEFAULT_TIMEOUT
                )
            else:
                return {
                    "status_code": None,
                    "message": f"Unsupported HTTP method: {method}",
                    "data": None,
                }
            response.raise_for_status()

            data = self._parse_response(response)

            return {
                "status_code": response.status_code,
                "data": data,
                "message": "API call successful.",
            }
        except requests.exceptions.Timeout:
            logger.error(f"Request timeout for {request_url}")
            return {
                "status_code": None,
                "message": f"Request timeout ({DEFAULT_TIMEOUT}s exceeded)",
                "data": None,
            }
        except requests.exceptions.ConnectionError as e:
            logger.error(f"Connection error: {str(e)}")
            return {
                "status_code": None,
                "message": f"Connection error: {str(e)}",
                "data": None,
            }
        except requests.exceptions.HTTPError as e:
            logger.error(f"HTTP error {response.status_code}: {str(e)}")
            try:
                error_data = response.json()
            except (json.JSONDecodeError, ValueError):
                error_data = response.text
            return {
                "status_code": response.status_code,
                "message": f"HTTP Error {response.status_code}",
                "data": error_data,
            }
        except requests.exceptions.RequestException as e:
            logger.error(f"Request failed: {str(e)}")
            return {
                "status_code": response.status_code if response else None,
                "message": f"API call failed: {str(e)}",
                "data": None,
            }
        except Exception as e:
            logger.error(f"Unexpected error in API call: {str(e)}", exc_info=True)
            return {
                "status_code": None,
                "message": f"Unexpected error: {str(e)}",
                "data": None,
            }

    def _parse_response(self, response: requests.Response) -> Any:
        """
        Parse response based on Content-Type header.

        Supports: JSON, XML, plain text, binary data.
        """
        content_type = response.headers.get("Content-Type", "").lower()

        if not response.content:
            return None
        # JSON response

        if "application/json" in content_type:
            try:
                return response.json()
            except json.JSONDecodeError as e:
                logger.warning(f"Failed to parse JSON response: {str(e)}")
                return response.text
        # XML response

        elif "application/xml" in content_type or "text/xml" in content_type:
            return response.text
        # Plain text response

        elif "text/plain" in content_type or "text/html" in content_type:
            return response.text
        # Binary/unknown response

        else:
            # Try to decode as text first, fall back to base64

            try:
                return response.text
            except (UnicodeDecodeError, AttributeError):
                import base64

                return base64.b64encode(response.content).decode("utf-8")

    def get_actions_metadata(self):
        """Return metadata for available actions (none for API Tool - actions are user-defined)."""
        return []

    def get_config_requirements(self):
        """Return configuration requirements for the tool."""
        return {}


================================================
FILE: application/agents/tools/base.py
================================================
from abc import ABC, abstractmethod


class Tool(ABC):
    @abstractmethod
    def execute_action(self, action_name: str, **kwargs):
        pass

    @abstractmethod
    def get_actions_metadata(self):
        """
        Returns a list of JSON objects describing the actions supported by the tool.
        """
        pass

    @abstractmethod
    def get_config_requirements(self):
        """
        Returns a dictionary describing the configuration requirements for the tool.
        """
        pass


================================================
FILE: application/agents/tools/brave.py
================================================
import logging

import requests

from application.agents.tools.base import Tool

logger = logging.getLogger(__name__)


class BraveSearchTool(Tool):
    """
    Brave Search
    A tool for performing web and image searches using the Brave Search API.
    Requires an API key for authentication.
    """

    def __init__(self, config):
        self.config = config
        self.token = config.get("token", "")
        self.base_url = "https://api.search.brave.com/res/v1"

    def execute_action(self, action_name, **kwargs):
        actions = {
            "brave_web_search": self._web_search,
            "brave_image_search": self._image_search,
        }

        if action_name in actions:
            return actions[action_name](**kwargs)
        else:
            raise ValueError(f"Unknown action: {action_name}")

    def _web_search(
        self,
        query,
        country="ALL",
        search_lang="en",
        count=10,
        offset=0,
        safesearch="off",
        freshness=None,
        result_filter=None,
        extra_snippets=False,
        summary=False,
    ):
        """
        Performs a web search using the Brave Search API.
        """
        logger.debug("Performing Brave web search for: %s", query)

        url = f"{self.base_url}/web/search"

        params = {
            "q": query,
            "country": country,
            "search_lang": search_lang,
            "count": min(count, 20),
            "offset": min(offset, 9),
            "safesearch": safesearch,
        }

        if freshness:
            params["freshness"] = freshness
        if result_filter:
            params["result_filter"] = result_filter
        if extra_snippets:
            params["extra_snippets"] = 1
        if summary:
            params["summary"] = 1
        headers = {
            "Accept": "application/json",
            "Accept-Encoding": "gzip",
            "X-Subscription-Token": self.token,
        }

        response = requests.get(url, params=params, headers=headers)

        if response.status_code == 200:
            return {
                "status_code": response.status_code,
                "results": response.json(),
                "message": "Search completed successfully.",
            }
        else:
            return {
                "status_code": response.status_code,
                "message": f"Search failed with status code: {response.status_code}.",
            }

    def _image_search(
        self,
        query,
        country="ALL",
        search_lang="en",
        count=5,
        safesearch="off",
        spellcheck=False,
    ):
        """
        Performs an image search using the Brave Search API.
        """
        logger.debug("Performing Brave image search for: %s", query)

        url = f"{self.base_url}/images/search"

        params = {
            "q": query,
            "country": country,
            "search_lang": search_lang,
            "count": min(count, 100),  # API max is 100
            "safesearch": safesearch,
            "spellcheck": 1 if spellcheck else 0,
        }

        headers = {
            "Accept": "application/json",
            "Accept-Encoding": "gzip",
            "X-Subscription-Token": self.token,
        }

        response = requests.get(url, params=params, headers=headers)

        if response.status_code == 200:
            return {
                "status_code": response.status_code,
                "results": response.json(),
                "message": "Image search completed successfully.",
            }
        else:
            return {
                "status_code": response.status_code,
                "message": f"Image search failed with status code: {response.status_code}.",
            }

    def get_actions_metadata(self):
        return [
            {
                "name": "brave_web_search",
                "description": "Perform a web search using Brave Search",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "The search query (max 400 characters, 50 words)",
                        },
                        "search_lang": {
                            "type": "string",
                            "description": "The search language preference (default: en)",
                        },
                        "freshness": {
                            "type": "string",
                            "description": "Time filter for results (pd: last 24h, pw: last week, pm: last month, py: last year)",
                        },
                    },
                    "required": ["query"],
                    "additionalProperties": False,
                },
            },
            {
                "name": "brave_image_search",
                "description": "Perform an image search using Brave Search",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "The search query (max 400 characters, 50 words)",
                        },
                        "count": {
                            "type": "integer",
                            "description": "Number of results to return (max 100, default: 5)",
                        },
                    },
                    "required": ["query"],
                    "additionalProperties": False,
                },
            },
        ]

    def get_config_requirements(self):
        return {
            "token": {
                "type": "string",
                "label": "API Key",
                "description": "Brave Search API key for authentication",
                "required": True,
                "secret": True,
                "order": 1,
            },
        }


================================================
FILE: application/agents/tools/cryptoprice.py
================================================
import requests
from application.agents.tools.base import Tool


class CryptoPriceTool(Tool):
    """
    CryptoPrice
    A tool for retrieving cryptocurrency prices using the CryptoCompare public API
    """

    def __init__(self, config):
        self.config = config

    def execute_action(self, action_name, **kwargs):
        actions = {"cryptoprice_get": self._get_price}

        if action_name in actions:
            return actions[action_name](**kwargs)
        else:
            raise ValueError(f"Unknown action: {action_name}")

    def _get_price(self, symbol, currency):
        """
        Fetches the current price of a given cryptocurrency symbol in the specified currency.
        Example:
            symbol = "BTC"
            currency = "USD"
            returns price in USD.
        """
        url = f"https://min-api.cryptocompare.com/data/price?fsym={symbol.upper()}&tsyms={currency.upper()}"
        response = requests.get(url)
        if response.status_code == 200:
            data = response.json()
            if currency.upper() in data:
                return {
                    "status_code": response.status_code,
                    "price": data[currency.upper()],
                    "message": f"Price of {symbol.upper()} in {currency.upper()} retrieved successfully.",
                }
            else:
                return {
                    "status_code": response.status_code,
                    "message": f"Couldn't find price for {symbol.upper()} in {currency.upper()}.",
                }
        else:
            return {
                "status_code": response.status_code,
                "message": "Failed to retrieve price.",
            }

    def get_actions_metadata(self):
        return [
            {
                "name": "cryptoprice_get",
                "description": "Retrieve the price of a specified cryptocurrency in a given currency",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "symbol": {
                            "type": "string",
                            "description": "The cryptocurrency symbol (e.g. BTC)",
                        },
                        "currency": {
                            "type": "string",
                            "description": "The currency in which you want the price (e.g. USD)",
                        },
                    },
                    "required": ["symbol", "currency"],
                    "additionalProperties": False,
                },
            }
        ]

    def get_config_requirements(self):
        # No specific configuration needed for this tool as it just queries a public endpoint
        return {}


================================================
FILE: application/agents/tools/duckduckgo.py
================================================
import logging
import time
from typing import Any, Dict, Optional

from application.agents.tools.base import Tool

logger = logging.getLogger(__name__)

MAX_RETRIES = 3
RETRY_DELAY = 2.0
DEFAULT_TIMEOUT = 15


class DuckDuckGoSearchTool(Tool):
    """
    DuckDuckGo Search
    A tool for performing web and image searches using DuckDuckGo.
    """

    def __init__(self, config):
        self.config = config
        self.timeout = config.get("timeout", DEFAULT_TIMEOUT)

    def _get_ddgs_client(self):
        from ddgs import DDGS

        return DDGS(timeout=self.timeout)

    def _execute_with_retry(self, operation, operation_name: str) -> Dict[str, Any]:
        last_error = None
        for attempt in range(1, MAX_RETRIES + 1):
            try:
                results = operation()
                return {
                    "status_code": 200,
                    "results": list(results) if results else [],
                    "message": f"{operation_name} completed successfully.",
                }
            except Exception as e:
                last_error = e
                error_str = str(e).lower()
                if "ratelimit" in error_str or "429" in error_str:
                    if attempt < MAX_RETRIES:
                        delay = RETRY_DELAY * attempt
                        logger.warning(
                            f"{operation_name} rate limited, retrying in {delay}s (attempt {attempt}/{MAX_RETRIES})"
                        )
                        time.sleep(delay)
                        continue
                logger.error(f"{operation_name} failed: {e}")
                break
        return {
            "status_code": 500,
            "results": [],
            "message": f"{operation_name} failed: {str(last_error)}",
        }

    def execute_action(self, action_name, **kwargs):
        actions = {
            "ddg_web_search": self._web_search,
            "ddg_image_search": self._image_search,
            "ddg_news_search": self._news_search,
        }
        if action_name not in actions:
            raise ValueError(f"Unknown action: {action_name}")
        return actions[action_name](**kwargs)

    def _web_search(
        self,
        query: str,
        max_results: int = 5,
        region: str = "wt-wt",
        safesearch: str = "moderate",
        timelimit: Optional[str] = None,
    ) -> Dict[str, Any]:
        logger.info(f"DuckDuckGo web search: {query}")

        def operation():
            client = self._get_ddgs_client()
            return client.text(
                query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=min(max_results, 20),
            )

        return self._execute_with_retry(operation, "Web search")

    def _image_search(
        self,
        query: str,
        max_results: int = 5,
        region: str = "wt-wt",
        safesearch: str = "moderate",
        timelimit: Optional[str] = None,
    ) -> Dict[str, Any]:
        logger.info(f"DuckDuckGo image search: {query}")

        def operation():
            client = self._get_ddgs_client()
            return client.images(
                query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=min(max_results, 50),
            )

        return self._execute_with_retry(operation, "Image search")

    def _news_search(
        self,
        query: str,
        max_results: int = 5,
        region: str = "wt-wt",
        safesearch: str = "moderate",
        timelimit: Optional[str] = None,
    ) -> Dict[str, Any]:
        logger.info(f"DuckDuckGo news search: {query}")

        def operation():
            client = self._get_ddgs_client()
            return client.news(
                query,
                region=region,
                safesearch=safesearch,
                timelimit=timelimit,
                max_results=min(max_results, 20),
            )

        return self._execute_with_retry(operation, "News search")

    def get_actions_metadata(self):
        return [
            {
                "name": "ddg_web_search",
                "description": "Search the web using DuckDuckGo. Returns titles, URLs, and snippets.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "Search query",
                        },
                        "max_results": {
                            "type": "integer",
                            "description": "Number of results (default: 5, max: 20)",
                        },
                        "region": {
                            "type": "string",
                            "description": "Region code (default: wt-wt for worldwide, us-en for US)",
                        },
                        "timelimit": {
                            "type": "string",
                            "description": "Time filter: d (day), w (week), m (month), y (year)",
                        },
                    },
                    "required": ["query"],
                },
            },
            {
                "name": "ddg_image_search",
                "description": "Search for images using DuckDuckGo. Returns image URLs and metadata.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "Image search query",
                        },
                        "max_results": {
                            "type": "integer",
                            "description": "Number of results (default: 5, max: 50)",
                        },
                        "region": {
                            "type": "string",
                            "description": "Region code (default: wt-wt for worldwide)",
                        },
                    },
                    "required": ["query"],
                },
            },
            {
                "name": "ddg_news_search",
                "description": "Search for news articles using DuckDuckGo. Returns recent news.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "query": {
                            "type": "string",
                            "description": "News search query",
                        },
                        "max_results": {
                            "type": "integer",
                            "description": "Number of results (default: 5, max: 20)",
                        },
                        "timelimit": {
                            "type": "string",
                            "description": "Time filter: d (day), w (week), m (month)",
                        },
                    },
                    "required": ["query"],
                },
            },
        ]

    def get_config_requirements(self):
        return {}


================================================
FILE: application/agents/tools/mcp_tool.py
================================================
import asyncio
import base64
import concurrent.futures
import json
import logging
import time
from typing import Any, Dict, List, Optional
from urllib.parse import parse_qs, urlparse

from fastmcp import Client
from fastmcp.client.auth import BearerAuth
from fastmcp.client.transports import (
    SSETransport,
    StdioTransport,
    StreamableHttpTransport,
)
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken
from pydantic import AnyHttpUrl, ValidationError
from redis import Redis

from application.agents.tools.base import Tool
from application.api.user.tasks import mcp_oauth_status_task, mcp_oauth_task
from application.cache import get_redis_instance
from application.core.mongo_db import MongoDB
from application.core.settings import settings
from application.security.encryption import decrypt_credentials

logger = logging.getLogger(__name__)

mongo = MongoDB.get_client()
db = mongo[settings.MONGO_DB_NAME]

_mcp_clients_cache = {}


class MCPTool(Tool):
    """
    MCP Tool
    Connect to remote Model Context Protocol (MCP) servers to access dynamic tools and resources.
    """

    def __init__(self, config: Dict[str, Any], user_id: Optional[str] = None):
        """
        Initialize the MCP Tool with configuration.

        Args:
            config: Dictionary containing MCP server configuration:
                - server_url: URL of the remote MCP server
                - transport_type: Transport type (auto, sse, http, stdio)
                - auth_type: Type of authentication (bearer, oauth, api_key, basic, none)
                - encrypted_credentials: Encrypted credentials (if available)
                - timeout: Request timeout in seconds (default: 30)
                - headers: Custom headers for requests
                - command: Command for STDIO transport
                - args: Arguments for STDIO transport
                - oauth_scopes: OAuth scopes for oauth auth type
                - oauth_client_name: OAuth client name for oauth auth type
                - query_mode: If True, use non-interactive OAuth (fail-fast on 401)
            user_id: User ID for decrypting credentials (required if encrypted_credentials exist)
        """
        self.config = config
        self.user_id = user_id
        self.server_url = config.get("server_url", "")
        self.transport_type = config.get("transport_type", "auto")
        self.auth_type = config.get("auth_type", "none")
        self.timeout = config.get("timeout", 30)
        self.custom_headers = config.get("headers", {})

        self.auth_credentials = {}
        if config.get("encrypted_credentials") and user_id:
            self.auth_credentials = decrypt_credentials(
                config["encrypted_credentials"], user_id
            )
        else:
            self.auth_credentials = config.get("auth_credentials", {})
        self.oauth_scopes = config.get("oauth_scopes", [])
        self.oauth_task_id = config.get("oauth_task_id", None)
        self.oauth_client_name = config.get("oauth_client_name", "DocsGPT-MCP")
        self.redirect_uri = self._resolve_redirect_uri(config.get("redirect_uri"))

        self.available_tools = []
        self._cache_key = self._generate_cache_key()
        self._client = None
        self.query_mode = config.get("query_mode", False)

        if self.server_url and self.auth_type != "oauth":
            self._setup_client()

    def _resolve_redirect_uri(self, configured_redirect_uri: Optional[str]) -> str:
        if configured_redirect_uri:
            return configured_redirect_uri.rstrip("/")

        explicit = getattr(settings, "MCP_OAUTH_REDIRECT_URI", None)
        if explicit:
            return explicit.rstrip("/")

        connector_base = getattr(settings, "CONNECTOR_REDIRECT_BASE_URI", None)
        if connector_base:
            parsed = urlparse(connector_base)
            if parsed.scheme and parsed.netloc:
                return f"{parsed.scheme}://{parsed.netloc}/api/mcp_server/callback"

        return f"{settings.API_URL.rstrip('/')}/api/mcp_server/callback"

    def _generate_cache_key(self) -> str:
        """Generate a unique cache key for this MCP server configuration."""
        auth_key = ""
        if self.auth_type == "oauth":
            scopes_str = ",".join(self.oauth_scopes) if self.oauth_scopes else "none"
            auth_key = (
                f"oauth:{self.oauth_client_name}:{scopes_str}:{self.redirect_uri}"
            )
        elif self.auth_type in ["bearer"]:
            token = self.auth_credentials.get(
                "bearer_token", ""
            ) or self.auth_credentials.get("access_token", "")
            auth_key = f"bearer:{token[:10]}..." if token else "bearer:none"
        elif self.auth_type == "api_key":
            api_key = self.auth_credentials.get("api_key", "")
            auth_key = f"apikey:{api_key[:10]}..." if api_key else "apikey:none"
        elif self.auth_type == "basic":
            username = self.auth_credentials.get("username", "")
            auth_key = f"basic:{username}"
        else:
            auth_key = "none"
        return f"{self.server_url}#{self.transport_type}#{auth_key}"

    def _setup_client(self):
        global _mcp_clients_cache
        if self._cache_key in _mcp_clients_cache:
            cached_data = _mcp_clients_cache[self._cache_key]
            if time.time() - cached_data["created_at"] < 300:
                self._client = cached_data["client"]
                return
            else:
                del _mcp_clients_cache[self._cache_key]
        transport = self._create_transport()
        auth = None

        if self.auth_type == "oauth":
            redis_client = get_redis_instance()
            if self.query_mode:
                auth = NonInteractiveOAuth(
                    mcp_url=self.server_url,
                    scopes=self.oauth_scopes,
                    redis_client=redis_client,
                    redirect_uri=self.redirect_uri,
                    db=db,
                    user_id=self.user_id,
                )
            else:
                auth = DocsGPTOAuth(
                    mcp_url=self.server_url,
                    scopes=self.oauth_scopes,
                    redis_client=redis_client,
                    redirect_uri=self.redirect_uri,
                    task_id=self.oauth_task_id,
                    db=db,
                    user_id=self.user_id,
                )
        elif self.auth_type == "bearer":
            token = self.auth_credentials.get(
                "bearer_token", ""
            ) or self.auth_credentials.get("access_token", "")
            if token:
                auth = BearerAuth(token)
        self._client = Client(transport, auth=auth)
        _mcp_clients_cache[self._cache_key] = {
            "client": self._client,
            "created_at": time.time(),
        }

    def _create_transport(self):
        """Create appropriate transport based on configuration."""
        headers = {"Content-Type": "application/json", "User-Agent": "DocsGPT-MCP/1.0"}
        headers.update(self.custom_headers)

        if self.auth_type == "api_key":
            api_key = self.auth_credentials.get("api_key", "")
            header_name = self.auth_credentials.get("api_key_header", "X-API-Key")
            if api_key:
                headers[header_name] = api_key
        elif self.auth_type == "basic":
            username = self.auth_credentials.get("username", "")
            password = self.auth_credentials.get("password", "")
            if username and password:
                credentials = base64.b64encode(
                    f"{username}:{password}".encode()
                ).decode()
                headers["Authorization"] = f"Basic {credentials}"
        if self.transport_type == "auto":
            if "sse" in self.server_url.lower() or self.server_url.endswith("/sse"):
                transport_type = "sse"
            else:
                transport_type = "http"
        else:
            transport_type = self.transport_type
        if transport_type == "stdio":
            raise ValueError("STDIO transport is disabled")
        if transport_type == "sse":
            headers.update({"Accept": "text/event-stream", "Cache-Control": "no-cache"})
            return SSETransport(url=self.server_url, headers=headers)
        elif transport_type == "http":
            return StreamableHttpTransport(url=self.server_url, headers=headers)
        elif transport_type == "stdio":
            command = self.config.get("command", "python")
            args = self.config.get("args", [])
            env = self.auth_credentials if self.auth_credentials else None
            return StdioTransport(command=command, args=args, env=env)
        else:
            return StreamableHttpTransport(url=self.server_url, headers=headers)

    def _format_tools(self, tools_response) -> List[Dict]:
        """Format tools response to match expected format."""
        if hasattr(tools_response, "tools"):
            tools = tools_response.tools
        elif isinstance(tools_response, list):
            tools = tools_response
        else:
            tools = []
        tools_dict = []
        for tool in tools:
            if hasattr(tool, "name"):
                tool_dict = {
                    "name": tool.name,
                    "description": tool.description,
                }
                if hasattr(tool, "inputSchema"):
                    tool_dict["inputSchema"] = tool.inputSchema
                tools_dict.append(tool_dict)
            elif isinstance(tool, dict):
                tools_dict.append(tool)
            else:
                if hasattr(tool, "model_dump"):
                    tools_dict.append(tool.model_dump())
                else:
                    tools_dict.append({"name": str(tool), "description": ""})
        return tools_dict

    async def _execute_with_client(self, operation: str, *args, **kwargs):
        """Execute operation with FastMCP client."""
        if not self._client:
            raise Exception("FastMCP client not initialized")
        async with self._client:
            if operation == "ping":
                return await self._client.ping()
            elif operation == "list_tools":
                tools_response = await self._client.list_tools()
                self.available_tools = self._format_tools(tools_response)
                return self.available_tools
            elif operation == "call_tool":
                tool_name = args[0]
                tool_args = kwargs
                return await self._client.call_tool(tool_name, tool_args)
            elif operation == "list_resources":
                return await self._client.list_resources()
            elif operation == "list_prompts":
                return await self._client.list_prompts()
            else:
                raise Exception(f"Unknown operation: {operation}")

    _ERROR_MAP = [
        (concurrent.futures.TimeoutError, lambda op, t, _: f"Timed out after {t}s"),
        (ConnectionRefusedError, lambda *_: "Connection refused"),
    ]

    _ERROR_PATTERNS = {
        ("403", "Forbidden"): "Access denied (403 Forbidden)",
        ("401", "Unauthorized"): "Authentication failed (401 Unauthorized)",
        ("ECONNREFUSED",): "Connection refused",
        ("SSL", "certificate"): "SSL/TLS error",
    }

    def _run_async_operation(self, operation: str, *args, **kwargs):
        try:
            try:
                asyncio.get_running_loop()
                with concurrent.futures.ThreadPoolExecutor() as executor:
                    future = executor.submit(
                        self._run_in_new_loop, operation, *args, **kwargs
                    )
                    return future.result(timeout=self.timeout)
            except RuntimeError:
                return self._run_in_new_loop(operation, *args, **kwargs)
        except Exception as e:
            raise self._map_error(operation, e) from e
            raise self._map_error(operation, e) from e

    def _run_in_new_loop(self, operation, *args, **kwargs):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            return loop.run_until_complete(
                self._execute_with_client(operation, *args, **kwargs)
            )
        finally:
            loop.close()

    def _map_error(self, operation: str, exc: Exception) -> Exception:
        for exc_type, msg_fn in self._ERROR_MAP:
            if isinstance(exc, exc_type):
                return Exception(msg_fn(operation, self.timeout, exc))
        error_msg = str(exc)
        for patterns, friendly in self._ERROR_PATTERNS.items():
            if any(p.lower() in error_msg.lower() for p in patterns):
                return Exception(friendly)
        logger.error("MCP %s failed: %s", operation, exc)
        return exc

    def discover_tools(self) -> List[Dict]:
        """
        Discover available tools from the MCP server using FastMCP.

        Returns:
            List of tool definitions from the server
        """
        if not self.server_url:
            return []
        if not self._client:
            self._setup_client()
        try:
            tools = self._run_async_operation("list_tools")
            self.available_tools = tools
            return self.available_tools
        except Exception as e:
            raise Exception(f"Failed to discover tools from MCP server: {str(e)}")

    def execute_action(self, action_name: str, **kwargs) -> Any:
        if not self.server_url:
            raise Exception("No MCP server configured")
        if not self._client:
            self._setup_client()
        cleaned_kwargs = {}
        for key, value in kwargs.items():
            if value == "" or value is None:
                continue
            cleaned_kwargs[key] = value
        try:
            result = self._run_async_operation(
                "call_tool", action_name, **cleaned_kwargs
            )
            return self._format_result(result)
        except Exception as e:
            error_msg = str(e)
            lower_msg = error_msg.lower()
            is_auth_error = (
                "401" in error_msg
                or "unauthorized" in lower_msg
                or "session expired" in lower_msg
                or "re-authorize" in lower_msg
            )
            if is_auth_error:
                if self.auth_type == "oauth":
                    raise Exception(
                        f"Action '{action_name}' failed: OAuth session expired. "
                        "Please re-authorize this MCP server in tool settings."
                    ) from e
                global _mcp_clients_cache
                _mcp_clients_cache.pop(self._cache_key, None)
                self._client = None
                self._setup_client()
                try:
                    result = self._run_async_operation(
                        "call_tool", action_name, **cleaned_kwargs
                    )
                    return self._format_result(result)
                except Exception as retry_e:
                    raise Exception(
                        f"Action '{action_name}' failed after re-auth attempt: {retry_e}. "
                        "Your credentials may have expired — please re-authorize in tool settings."
                    ) from retry_e
            raise Exception(
                f"Failed to execute action '{action_name}': {error_msg}"
            ) from e

    def _format_result(self, result) -> Dict:
        """Format FastMCP result to match expected format."""
        if hasattr(result, "content"):
            content_list = []
            for content_item in result.content:
                if hasattr(content_item, "text"):
                    content_list.append({"type": "text", "text": content_item.text})
                elif hasattr(content_item, "data"):
                    content_list.append({"type": "data", "data": content_item.data})
                else:
                    content_list.append(
                        {"type": "unknown", "content": str(content_item)}
                    )
            return {
                "content": content_list,
                "isError": getattr(result, "isError", False),
            }
        else:
            return result

    def test_connection(self) -> Dict:
        if not self.server_url:
            return {
                "success": False,
                "message": "No server URL configured",
                "tools_count": 0,
            }
        try:
            parsed = urlparse(self.server_url)
            if parsed.scheme not in ("http", "https"):
                return {
                    "success": False,
                    "message": f"Invalid URL scheme '{parsed.scheme}' — use http:// or https://",
                    "tools_count": 0,
                }
        except Exception:
            return {
                "success": False,
                "message": "Invalid URL format",
                "tools_count": 0,
            }
        if not self._client:
            try:
                self._setup_client()
            except Exception as e:
                return {
                    "success": False,
                    "message": f"Client init failed: {str(e)}",
                    "tools_count": 0,
                }
        try:
            if self.auth_type == "oauth":
                return self._test_oauth_connection()
            else:
                return self._test_regular_connection()
        except Exception as e:
            return {
                "success": False,
                "message": f"Connection failed: {str(e)}",
                "tools_count": 0,
            }

    def _test_regular_connection(self) -> Dict:
        ping_ok = False
        ping_error = None
        try:
            self._run_async_operation("ping")
            ping_ok = True
        except Exception as e:
            ping_error = str(e)

        try:
            tools = self.discover_tools()
        except Exception as e:
            return {
                "success": False,
                "message": f"Connection failed: {ping_error or str(e)}",
                "tools_count": 0,
            }

        if not tools and not ping_ok:
            return {
                "success": False,
                "message": f"Connection failed: {ping_error or 'No tools found'}",
                "tools_count": 0,
            }

        return {
            "success": True,
            "message": f"Connected — found {len(tools)} tool{'s' if len(tools) != 1 else ''}.",
            "tools_count": len(tools),
            "tools": [
                {
                    "name": tool.get("name", "unknown"),
                    "description": tool.get("description", ""),
                }
                for tool in tools
            ],
        }

    def _test_oauth_connection(self) -> Dict:
        storage = DBTokenStorage(
            server_url=self.server_url, user_id=self.user_id, db_client=db
        )
        loop = asyncio.new_event_loop()
        try:
            tokens = loop.run_until_complete(storage.get_tokens())
        finally:
            loop.close()

        if tokens and tokens.access_token:
            self.query_mode = True
            _mcp_clients_cache.pop(self._cache_key, None)
            self._client = None
            self._setup_client()
            try:
                tools = self.discover_tools()
                return {
                    "success": True,
                    "message": f"Connected — found {len(tools)} tool{'s' if len(tools) != 1 else ''}.",
                    "tools_count": len(tools),
                    "tools": [
                        {
                            "name": t.get("name", "unknown"),
                            "description": t.get("description", ""),
                        }
                        for t in tools
                    ],
                }
            except Exception as e:
                logger.warning("OAuth token validation failed: %s", e)
                _mcp_clients_cache.pop(self._cache_key, None)
                self._client = None

        return self._start_oauth_task()

    def _start_oauth_task(self) -> Dict:
        task_config = self.config.copy()
        task_config.pop("query_mode", None)
        result = mcp_oauth_task.delay(task_config, self.user_id)
        return {
            "success": False,
            "requires_oauth": True,
            "task_id": result.id,
            "message": "OAuth authorization required.",
            "tools_count": 0,
        }

    def get_actions_metadata(self) -> List[Dict]:
        """
        Get metadata for all available actions.

        Returns:
            List of action metadata dictionaries
        """
        actions = []
        for tool in self.available_tools:
            input_schema = (
                tool.get("inputSchema")
                or tool.get("input_schema")
                or tool.get("schema")
                or tool.get("parameters")
            )

            parameters_schema = {
                "type": "object",
                "properties": {},
                "required": [],
            }

            if input_schema:
                if isinstance(input_schema, dict):
                    if "properties" in input_schema:
                        parameters_schema = {
                            "type": input_schema.get("type", "object"),
                            "properties": input_schema.get("properties", {}),
                            "required": input_schema.get("required", []),
                        }

                        for key in ["additionalProperties", "description"]:
                            if key in input_schema:
                                parameters_schema[key] = input_schema[key]
                    else:
                        parameters_schema["properties"] = input_schema
            action = {
                "name": tool.get("name", ""),
                "description": tool.get("description", ""),
                "parameters": parameters_schema,
            }
            actions.append(action)
        return actions

    def get_config_requirements(self) -> Dict:
        return {
            "server_url": {
                "type": "string",
                "label": "Server URL",
                "description": "URL of the remote MCP server",
                "required": True,
                "secret": False,
                "order": 1,
            },
            "auth_type": {
                "type": "string",
                "label": "Authentication Type",
                "description": "Authentication method for the MCP server",
                "enum": ["none", "bearer", "oauth", "api_key", "basic"],
                "default": "none",
                "required": True,
                "secret": False,
                "order": 2,
            },
            "api_key": {
                "type": "string",
                "label": "API Key",
                "description": "API key for authentication",
                "required": False,
                "secret": True,
                "order": 3,
                "depends_on": {"auth_type": "api_key"},
            },
            "api_key_header": {
                "type": "string",
                "label": "API Key Header",
                "description": "Header name for API key (default: X-API-Key)",
                "default": "X-API-Key",
                "required": False,
                "secret": False,
                "order": 4,
                "depends_on": {"auth_type": "api_key"},
            },
            "bearer_token": {
                "type": "string",
                "label": "Bearer Token",
                "description": "Bearer token for authentication",
                "required": False,
                "secret": True,
                "order": 3,
                "depends_on": {"auth_type": "bearer"},
            },
            "username": {
                "type": "string",
                "label": "Username",
                "description": "Username for basic authentication",
                "required": False,
                "secret": False,
                "order": 3,
                "depends_on": {"auth_type": "basic"},
            },
            "password": {
                "type": "string",
                "label": "Password",
                "description": "Password for basic authentication",
                "required": False,
                "secret": True,
                "order": 4,
                "depends_on": {"auth_type": "basic"},
            },
            "oauth_scopes": {
                "type": "string",
                "label": "OAuth Scopes",
                "description": "Comma-separated OAuth scopes to request",
                "required": False,
                "secret": False,
                "order": 3,
                "depends_on": {"auth_type": "oauth"},
            },
            "timeout": {
                "type": "number",
                "label": "Timeout (seconds)",
                "description": "Request timeout in seconds (1-300)",
                "default": 30,
                "required": False,
                "secret": False,
                "order": 10,
            },
        }


class DocsGPTOAuth(OAuthClientProvider):
    """
    Custom OAuth handler for DocsGPT that uses frontend redirect instead of browser.
    """

    def __init__(
        self,
        mcp_url: str,
        redirect_uri: str,
        redis_client: Redis | None = None,
        redis_prefix: str = "mcp_oauth:",
        task_id: str = None,
        scopes: str | list[str] | None = None,
        client_name: str = "DocsGPT-MCP",
        user_id=None,
        db=None,
        additional_client_metadata: dict[str, Any] | None = None,
        skip_redirect_validation: bool = False,
    ):
        self.redirect_uri = redirect_uri
        self.redis_client = redis_client
        self.redis_prefix = redis_prefix
        self.task_id = task_id
        self.user_id = user_id
        self.db = db

        parsed_url = urlparse(mcp_url)
        self.server_base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"

        if isinstance(scopes, list):
            scopes = " ".join(scopes)
        client_metadata = OAuthClientMetadata(
            client_name=client_name,
            redirect_uris=[AnyHttpUrl(redirect_uri)],
            grant_types=["authorization_code", "refresh_token"],
            response_types=["code"],
            scope=scopes,
            **(additional_client_metadata or {}),
        )

        storage = DBTokenStorage(
            server_url=self.server_base_url,
            user_id=self.user_id,
            db_client=self.db,
            expected_redirect_uri=None if skip_redirect_validation else redirect_uri,
        )

        super().__init__(
            server_url=self.server_base_url,
            client_metadata=client_metadata,
            storage=storage,
            redirect_handler=self.redirect_handler,
            callback_handler=self.callback_handler,
        )

        self.auth_url = None
        self.extracted_state = None

    def _process_auth_url(self, authorization_url: str) -> tuple[str, str]:
        """Process authorization URL to extract state"""
        try:
            parsed_url = urlparse(authorization_url)
            query_params = parse_qs(parsed_url.query)

            state_params = query_params.get("state", [])
            if state_params:
                state = state_params[0]
            else:
                raise ValueError("No state in auth URL")
            return authorization_url, state
        except Exception as e:
            raise Exception(f"Failed to process auth URL: {e}")

    async def redirect_handler(self, authorization_url: str) -> None:
        """Store auth URL and state in Redis for frontend to use."""
        auth_url, state = self._process_auth_url(authorization_url)
        logger.info("Processed auth_url: %s, state: %s", auth_url, state)
        self.auth_url = auth_url
        self.extracted_state = state

        if self.redis_client and self.extracted_state:
            key = f"{self.redis_prefix}auth_url:{self.extracted_state}"
            self.redis_client.setex(key, 600, auth_url)
            logger.info("Stored auth_url in Redis: %s", key)

            if self.task_id:
                status_key = f"mcp_oauth_status:{self.task_id}"
                status_data = {
                    "status": "requires_redirect",
                    "message": "Authorization required",
                    "authorization_url": self.auth_url,
                    "state": self.extracted_state,
                    "requires_oauth": True,
                    "task_id": self.task_id,
                }
                self.redis_client.setex(status_key, 600, json.dumps(status_data))

    async def callback_handler(self) -> tuple[str, str | None]:
        """Wait for auth code from Redis using the state value."""
        if not self.redis_client or not self.extracted_state:
            raise Exception("Redis client or state not configured for OAuth")
        poll_interval = 1
        max_wait_time = 300
        code_key = f"{self.redis_prefix}code:{self.extracted_state}"

        if self.task_id:
            status_key = f"mcp_oauth_status:{self.task_id}"
            status_data = {
                "status": "awaiting_callback",
                "message": "Waiting for authorization...",
                "authorization_url": self.auth_url,
                "state": self.extracted_state,
                "requires_oauth": True,
                "task_id": self.task_id,
            }
            self.redis_client.setex(status_key, 600, json.dumps(status_data))
        start_time = time.time()
        while time.time() - start_time < max_wait_time:
            code_data = self.redis_client.get(code_key)
            if code_data:
                code = code_data.decode()
                returned_state = self.extracted_state

                self.redis_client.delete(code_key)
                self.redis_client.delete(
                    f"{self.redis_prefix}auth_url:{self.extracted_state}"
                )
                self.redis_client.delete(
                    f"{self.redis_prefix}state:{self.extracted_state}"
                )

                if self.task_id:
                    status_data = {
                        "status": "callback_received",
                        "message": "Completing authentication...",
                        "task_id": self.task_id,
                    }
                    self.redis_client.setex(status_key, 600, json.dumps(status_data))
                return code, returned_state
            error_key = f"{self.redis_prefix}error:{self.extracted_state}"
            error_data = self.redis_client.get(error_key)
            if error_data:
                error_msg = error_data.decode()
                self.redis_client.delete(error_key)
                self.redis_client.delete(
                    f"{self.redis_prefix}auth_url:{self.extracted_state}"
                )
                self.redis_client.delete(
                    f"{self.redis_prefix}state:{self.extracted_state}"
                )
                raise Exception(f"OAuth error: {error_msg}")
            await asyncio.sleep(poll_interval)
        self.redis_client.delete(f"{self.redis_prefix}auth_url:{self.extracted_state}")
        self.redis_client.delete(f"{self.redis_prefix}state:{self.extracted_state}")
        raise Exception("OAuth timeout: no code received within 5 minutes")


class NonInteractiveOAuth(DocsGPTOAuth):
    """OAuth provider that fails fast on 401 instead of starting interactive auth.

    Used during query execution to prevent the streaming response from blocking
    while waiting for user authorization that will never come.
    """

    def __init__(self, **kwargs):
        kwargs.setdefault("task_id", None)
        kwargs["skip_redirect_validation"] = True
        super().__init__(**kwargs)

    async def redirect_handler(self, authorization_url: str) -> None:
        raise Exception(
            "OAuth session expired — please re-authorize this MCP server in tool settings."
        )

    async def callback_handler(self) -> tuple[str, str | None]:
        raise Exception(
            "OAuth session expired — please re-authorize this MCP server in tool settings."
        )


class DBTokenStorage(TokenStorage):
    def __init__(
        self,
        server_url: str,
        user_id: str,
        db_client,
        expected_redirect_uri: Optional[str] = None,
    ):
        self.server_url = server_url
        self.user_id = user_id
        self.db_client = db_client
        self.expected_redirect_uri = expected_redirect_uri
        self.collection = db_client["connector_sessions"]

    @staticmethod
    def get_base_url(url: str) -> str:
        parsed = urlparse(url)
        return f"{parsed.scheme}://{parsed.netloc}"

    def get_db_key(self) -> dict:
        return {
            "server_url": self.get_base_url(self.server_url),
            "user_id": self.user_id,
        }

    async def get_tokens(self) -> OAuthToken | None:
        doc = await asyncio.to_thread(self.collection.find_one, self.get_db_key())
        if not doc or "tokens" not in doc:
            return None
        try:
            return OAuthToken.model_validate(doc["tokens"])
        except ValidationError as e:
            logger.error("Could not load tokens: %s", e)
            return None

    async def set_tokens(self, tokens: OAuthToken) -> None:
        await asyncio.to_thread(
            self.collection.update_one,
            self.get_db_key(),
            {"$set": {"tokens": tokens.model_dump()}},
            True,
        )
        logger.info("Saved tokens for %s", self.get_base_url(self.server_url))

    async def get_client_info(self) -> OAuthClientInformationFull | None:
        doc = await asyncio.to_thread(self.collection.find_one, self.get_db_key())
        if not doc or "client_info" not in doc:
            logger.debug(
                "No client_info in DB for %s", self.get_base_url(self.server_url)
            )
            return None
        try:
            client_info = OAuthClientInformationFull.model_validate(doc["client_info"])
            if self.expected_redirect_uri:
                stored_uris = [
                    str(uri).rstrip("/") for uri in client_info.redirect_uris
                ]
                expected_uri = self.expected_redirect_uri.rstrip("/")
                if expected_uri not in stored_uris:
                    logger.warning(
                        "Redirect URI mismatch for %s: expected=%s stored=%s — clearing.",
                        self.get_base_url(self.server_url),
                        expected_uri,
                        stored_uris,
                    )
                    await asyncio.to_thread(
                        self.collection.update_one,
                        self.get_db_key(),
                        {"$unset": {"client_info": "", "tokens": ""}},
                    )
                    return None
            return client_info
        except ValidationError as e:
            logger.error("Could not load client info: %s", e)
            return None

    def _serialize_client_info(self, info: dict) -> dict:
        if "redirect_uris" in info and isinstance(info["redirect_uris"], list):
            info["redirect_uris"] = [str(u) for u in info["redirect_uris"]]
        return info

    async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
        serialized_info = self._serialize_client_info(client_info.model_dump())
        await asyncio.to_thread(
            self.collection.update_one,
            self.get_db_key(),
            {"$set": {"client_info": serialized_info}},
            True,
        )
        logger.info("Saved client info for %s", self.get_base_url(self.server_url))

    async def clear(self) -> None:
        await asyncio.to_thread(self.collection.delete_one, self.get_db_key())
        logger.info("Cleared OAuth cache for %s", self.get_base_url(self.server_url))

    @classmethod
    async def clear_all(cls, db_client) -> None:
        collection = db_client["connector_sessions"]
        await asyncio.to_thread(collection.delete_many, {})
        logger.info("Cleared all OAuth client cache data.")


class MCPOAuthManager:
    """Manager for handling MCP OAuth callbacks."""

    def __init__(self, redis_client: Redis | None, redis_prefix: str = "mcp_oauth:"):
        self.redis_client = redis_client
        self.redis_prefix = redis_prefix

    def handle_oauth_callback(
        self, state: str, code: str, error: Optional[str] = None
    ) -> bool:
        """
        Handle OAuth callback from provider.

        Args:
            state: The state parameter from OAuth callback
            code: The authorization code from OAuth callback
            error: Error message if OAuth failed

        Returns:
            True if successful, False otherwise
        """
        try:
            if not self.redis_client or not state:
                raise Exception("Redis client or state not provided")
            if error:
                error_key = f"{self.redis_prefix}error:{state}"
                self.redis_client.setex(error_key, 300, error)
                raise Exception(f"OAuth error received: {error}")
            code_key = f"{self.redis_prefix}code:{state}"
            self.redis_client.setex(code_key, 300, code)

            state_key = f"{self.redis_prefix}state:{state}"
            self.redis_client.setex(state_key, 300, "completed")

            return True
        except Exception as e:
            logger.error("Error handling OAuth callback: %s", e)
            return False

    def get_oauth_status(self, task_id: str) -> Dict[str, Any]:
        """Get current status of OAuth flow using provided task_id."""
        if not task_id:
            return {"status": "not_started", "message": "OAuth flow not started"}
        return mcp_oauth_status_task(task_id)


================================================
FILE: application/agents/tools/memory.py
================================================
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional
import re
import uuid

from .base import Tool
from application.core.mongo_db import MongoDB
from application.core.settings import settings


class MemoryTool(Tool):
    """Memory

    Stores and retrieves information across conversations through a memory file directory.
    """

    def __init__(self, tool_config: Optional[Dict[str, Any]] = None, user_id: Optional[str] = None) -> None:
        """Initialize the tool.

        Args:
            tool_config: Optional tool configuration. Should include:
                - tool_id: Unique identifier for this memory tool instance (from user_tools._id)
                           This ensures each user's tool configuration has isolated memories
            user_id: The authenticated user's id (should come from decoded_token["sub"]).
        """
        self.user_id: Optional[str] = user_id

        # Get tool_id from configuration (passed from user_tools._id in production)
        # In production, tool_id is the MongoDB ObjectId string from user_tools collection
        if tool_config and "tool_id" in tool_config:
            self.tool_id = tool_config["tool_id"]
        elif user_id:
            # Fallback for backward compatibility or testing
            self.tool_id = f"default_{user_id}"
        else:
            # Last resort fallback (shouldn't happen in normal use)
            self.tool_id = str(uuid.uuid4())

        db = MongoDB.get_client()[settings.MONGO_DB_NAME]
        self.collection = db["memories"]

    # -----------------------------
    # Action implementations
    # -----------------------------
    def execute_action(self, action_name: str, **kwargs: Any) -> str:
        """Execute an action by name.

        Args:
            action_name: One of view, create, str_replace, insert, delete, rename.
            **kwargs: Parameters for the action.

        Returns:
            A human-readable string result.
        """
        if not self.user_id:
            return "Error: MemoryTool requires a valid user_id."

        if action_name == "view":
            return self._view(
                kwargs.get("path", "/"),
                kwargs.get("view_range")
            )

        if action_name == "create":
            return self._create(
                kwargs.get("path", ""),
                kwargs.get("file_text", "")
            )

        if action_name == "str_replace":
            return self._str_replace(
                kwargs.get("path", ""),
                kwargs.get("old_str", ""),
                kwargs.get("new_str", "")
            )

        if action_name == "insert":
            return self._insert(
                kwargs.get("path", ""),
                kwargs.get("insert_line", 1),
                kwargs.get("insert_text", "")
            )

        if action_name == "delete":
            return self._delete(kwargs.get("path", ""))

        if action_name == "rename":
            return self._rename(
                kwargs.get("old_path", ""),
                kwargs.get("new_path", "")
            )

        return f"Unknown action: {action_name}"

    def get_actions_metadata(self) -> List[Dict[str, Any]]:
        """Return JSON metadata describing supported actions for tool schemas."""
        return [
            {
                "name": "view",
                "description": "Shows directory contents or file contents with optional line ranges.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": "Path to file or directory (e.g., /notes.txt or /project/ or /)."
                        },
                        "view_range": {
                            "type": "array",
                            "items": {"type": "integer"},
                            "description": "Optional [start_line, end_line] to view specific lines (1-indexed)."
                        }
                    },
                    "required": ["path"]
                },
            },
            {
                "name": "create",
                "description": "Create or overwrite a file.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": "File path to create (e.g., /notes.txt or /project/task.txt)."
                        },
                        "file_text": {
                            "type": "string",
                            "description": "Content to write to the file."
                        }
                    },
                    "required": ["path", "file_text"]
                },
            },
            {
                "name": "str_replace",
                "description": "Replace text in a file.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": "File path (e.g., /notes.txt)."
                        },
                        "old_str": {
                            "type": "string",
                            "description": "String to find."
                        },
                        "new_str": {
                            "type": "string",
                            "description": "String to replace with."
                        }
                    },
                    "required": ["path", "old_str", "new_str"]
                },
            },
            {
                "name": "insert",
                "description": "Insert text at a specific line in a file.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": "File path (e.g., /notes.txt)."
                        },
                        "insert_line": {
                            "type": "integer",
                            "description": "Line number to insert at (1-indexed)."
                        },
                        "insert_text": {
                            "type": "string",
                            "description": "Text to insert."
                        }
                    },
                    "required": ["path", "insert_line", "insert_text"]
                },
            },
            {
                "name": "delete",
                "description": "Delete a file or directory.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": "Path to delete (e.g., /notes.txt or /project/)."
                        }
                    },
                    "required": ["path"]
                },
            },
            {
                "name": "rename",
                "description": "Rename or move a file/directory.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "old_path": {
                            "type": "string",
                            "description": "Current path (e.g., /old.txt)."
                        },
                        "new_path": {
                            "type": "string",
                            "description": "New path (e.g., /new.txt)."
                        }
                    },
                    "required": ["old_path", "new_path"]
                },
            },
        ]

    def get_config_requirements(self) -> Dict[str, Any]:
        """Return configuration requirements."""
        return {}

   
Download .txt
gitextract_kso0ck8g/

├── .devcontainer/
│   ├── Dockerfile
│   ├── devc-welcome.md
│   ├── devcontainer.json
│   ├── docker-compose-dev.yaml
│   ├── docker-compose.override.yaml
│   └── post-create-command.sh
├── .env-template
├── .gitattributes
├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   ├── dependabot.yml
│   ├── holopin.yml
│   ├── labeler.yml
│   ├── styles/
│   │   ├── DocsGPT/
│   │   │   └── Spelling.yml
│   │   └── config/
│   │       └── vocabularies/
│   │           └── DocsGPT/
│   │               └── accept.txt
│   └── workflows/
│       ├── bandit.yaml
│       ├── ci.yml
│       ├── cife.yml
│       ├── docker-develop-build.yml
│       ├── docker-develop-fe-build.yml
│       ├── labeler.yml
│       ├── lint.yml
│       ├── pytest.yml
│       ├── sync_fork.yaml
│       └── vale.yml
├── .gitignore
├── .ruff.toml
├── .vale.ini
├── .vscode/
│   └── launch.json
├── AGENTS.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── HACKTOBERFEST.md
├── LICENSE
├── README.md
├── SECURITY.md
├── application/
│   ├── Dockerfile
│   ├── __init__.py
│   ├── agents/
│   │   ├── __init__.py
│   │   ├── agent_creator.py
│   │   ├── base.py
│   │   ├── classic_agent.py
│   │   ├── react_agent.py
│   │   ├── tools/
│   │   │   ├── api_body_serializer.py
│   │   │   ├── api_tool.py
│   │   │   ├── base.py
│   │   │   ├── brave.py
│   │   │   ├── cryptoprice.py
│   │   │   ├── duckduckgo.py
│   │   │   ├── mcp_tool.py
│   │   │   ├── memory.py
│   │   │   ├── notes.py
│   │   │   ├── ntfy.py
│   │   │   ├── postgres.py
│   │   │   ├── read_webpage.py
│   │   │   ├── spec_parser.py
│   │   │   ├── telegram.py
│   │   │   ├── todo_list.py
│   │   │   ├── tool_action_parser.py
│   │   │   └── tool_manager.py
│   │   ├── workflow_agent.py
│   │   └── workflows/
│   │       ├── cel_evaluator.py
│   │       ├── node_agent.py
│   │       ├── schemas.py
│   │       └── workflow_engine.py
│   ├── api/
│   │   ├── __init__.py
│   │   ├── answer/
│   │   │   ├── __init__.py
│   │   │   ├── routes/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── answer.py
│   │   │   │   ├── base.py
│   │   │   │   ├── search.py
│   │   │   │   └── stream.py
│   │   │   └── services/
│   │   │       ├── __init__.py
│   │   │       ├── compression/
│   │   │       │   ├── __init__.py
│   │   │       │   ├── message_builder.py
│   │   │       │   ├── orchestrator.py
│   │   │       │   ├── prompt_builder.py
│   │   │       │   ├── service.py
│   │   │       │   ├── threshold_checker.py
│   │   │       │   ├── token_counter.py
│   │   │       │   └── types.py
│   │   │       ├── conversation_service.py
│   │   │       ├── prompt_renderer.py
│   │   │       └── stream_processor.py
│   │   ├── connector/
│   │   │   └── routes.py
│   │   ├── internal/
│   │   │   ├── __init__.py
│   │   │   └── routes.py
│   │   └── user/
│   │       ├── __init__.py
│   │       ├── agents/
│   │       │   ├── __init__.py
│   │       │   ├── folders.py
│   │       │   ├── routes.py
│   │       │   ├── sharing.py
│   │       │   └── webhooks.py
│   │       ├── analytics/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── attachments/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── base.py
│   │       ├── conversations/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── models/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── prompts/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── routes.py
│   │       ├── sharing/
│   │       │   ├── __init__.py
│   │       │   └── routes.py
│   │       ├── sources/
│   │       │   ├── __init__.py
│   │       │   ├── chunks.py
│   │       │   ├── routes.py
│   │       │   └── upload.py
│   │       ├── tasks.py
│   │       ├── tools/
│   │       │   ├── __init__.py
│   │       │   ├── mcp.py
│   │       │   └── routes.py
│   │       ├── utils.py
│   │       └── workflows/
│   │           ├── __init__.py
│   │           └── routes.py
│   ├── app.py
│   ├── auth.py
│   ├── cache.py
│   ├── celery_init.py
│   ├── celeryconfig.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── json_schema_utils.py
│   │   ├── logging_config.py
│   │   ├── model_configs.py
│   │   ├── model_settings.py
│   │   ├── model_utils.py
│   │   ├── mongo_db.py
│   │   ├── settings.py
│   │   └── url_validation.py
│   ├── error.py
│   ├── index.faiss
│   ├── index.pkl
│   ├── llm/
│   │   ├── __init__.py
│   │   ├── anthropic.py
│   │   ├── base.py
│   │   ├── docsgpt_provider.py
│   │   ├── google_ai.py
│   │   ├── groq.py
│   │   ├── handlers/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── google.py
│   │   │   ├── handler_creator.py
│   │   │   └── openai.py
│   │   ├── llama_cpp.py
│   │   ├── llm_creator.py
│   │   ├── novita.py
│   │   ├── open_router.py
│   │   ├── openai.py
│   │   ├── premai.py
│   │   └── sagemaker.py
│   ├── logging.py
│   ├── parser/
│   │   ├── __init__.py
│   │   ├── chunking.py
│   │   ├── connectors/
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── connector_creator.py
│   │   │   ├── google_drive/
│   │   │   │   ├── __init__.py
│   │   │   │   ├── auth.py
│   │   │   │   └── loader.py
│   │   │   └── share_point/
│   │   │       ├── __init__.py
│   │   │       ├── auth.py
│   │   │       └── loader.py
│   │   ├── embedding_pipeline.py
│   │   ├── file/
│   │   │   ├── __init__.py
│   │   │   ├── audio_parser.py
│   │   │   ├── base.py
│   │   │   ├── base_parser.py
│   │   │   ├── bulk.py
│   │   │   ├── constants.py
│   │   │   ├── docling_parser.py
│   │   │   ├── docs_parser.py
│   │   │   ├── epub_parser.py
│   │   │   ├── html_parser.py
│   │   │   ├── image_parser.py
│   │   │   ├── json_parser.py
│   │   │   ├── markdown_parser.py
│   │   │   ├── openapi3_parser.py
│   │   │   ├── pptx_parser.py
│   │   │   ├── rst_parser.py
│   │   │   └── tabular_parser.py
│   │   ├── remote/
│   │   │   ├── base.py
│   │   │   ├── crawler_loader.py
│   │   │   ├── crawler_markdown.py
│   │   │   ├── github_loader.py
│   │   │   ├── reddit_loader.py
│   │   │   ├── remote_creator.py
│   │   │   ├── s3_loader.py
│   │   │   ├── sitemap_loader.py
│   │   │   ├── telegram.py
│   │   │   └── web_loader.py
│   │   └── schema/
│   │       ├── __init__.py
│   │       ├── base.py
│   │       └── schema.py
│   ├── prompts/
│   │   ├── chat_combine_creative.txt
│   │   ├── chat_combine_default.txt
│   │   ├── chat_combine_strict.txt
│   │   ├── chat_reduce_prompt.txt
│   │   ├── compression/
│   │   │   └── v1.0.txt
│   │   ├── react_final_prompt.txt
│   │   └── react_planning_prompt.txt
│   ├── requirements.txt
│   ├── retriever/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── classic_rag.py
│   │   └── retriever_creator.py
│   ├── security/
│   │   ├── __init__.py
│   │   └── encryption.py
│   ├── seed/
│   │   ├── __init__.py
│   │   ├── commands.py
│   │   ├── config/
│   │   │   ├── agents_template.yaml
│   │   │   └── premade_agents.yaml
│   │   └── seeder.py
│   ├── storage/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── local.py
│   │   ├── s3.py
│   │   └── storage_creator.py
│   ├── stt/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── constants.py
│   │   ├── faster_whisper_stt.py
│   │   ├── live_session.py
│   │   ├── openai_stt.py
│   │   ├── stt_creator.py
│   │   └── upload_limits.py
│   ├── templates/
│   │   ├── __init__.py
│   │   ├── namespaces.py
│   │   └── template_engine.py
│   ├── tts/
│   │   ├── base.py
│   │   ├── elevenlabs.py
│   │   ├── google_tts.py
│   │   └── tts_creator.py
│   ├── usage.py
│   ├── utils.py
│   ├── vectorstore/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── document_class.py
│   │   ├── elasticsearch.py
│   │   ├── embeddings_local.py
│   │   ├── faiss.py
│   │   ├── lancedb.py
│   │   ├── milvus.py
│   │   ├── mongodb.py
│   │   ├── pgvector.py
│   │   ├── qdrant.py
│   │   └── vector_creator.py
│   ├── worker.py
│   └── wsgi.py
├── codecov.yml
├── deployment/
│   ├── docker-compose-azure.yaml
│   ├── docker-compose-dev.yaml
│   ├── docker-compose-hub.yaml
│   ├── docker-compose-local.yaml
│   ├── docker-compose.yaml
│   ├── k8s/
│   │   ├── deployments/
│   │   │   ├── docsgpt-deploy.yaml
│   │   │   ├── mongo-deploy.yaml
│   │   │   ├── qdrant-deploy.yaml
│   │   │   └── redis-deploy.yaml
│   │   ├── docsgpt-secrets.yaml
│   │   └── services/
│   │       ├── docsgpt-service.yaml
│   │       ├── mongo-service.yaml
│   │       ├── qdrant-service.yaml
│   │       └── redis-service.yaml
│   └── optional/
│       ├── docker-compose.optional.ollama-cpu.yaml
│       └── docker-compose.optional.ollama-gpu.yaml
├── docs/
│   ├── README.md
│   ├── app/
│   │   ├── [[...mdxPath]]/
│   │   │   └── page.jsx
│   │   └── layout.jsx
│   ├── components/
│   │   ├── DeploymentCards.jsx
│   │   └── ToolCards.jsx
│   ├── content/
│   │   ├── Agents/
│   │   │   ├── _meta.js
│   │   │   ├── api.mdx
│   │   │   ├── basics.mdx
│   │   │   ├── nodes.mdx
│   │   │   └── webhooks.mdx
│   │   ├── Deploying/
│   │   │   ├── Amazon-Lightsail.mdx
│   │   │   ├── Development-Environment.mdx
│   │   │   ├── Docker-Deploying.mdx
│   │   │   ├── DocsGPT-Settings.mdx
│   │   │   ├── Hosting-the-app.mdx
│   │   │   ├── Kubernetes-Deploying.mdx
│   │   │   ├── Railway.mdx
│   │   │   └── _meta.js
│   │   ├── Extensions/
│   │   │   ├── Chatwoot-extension.mdx
│   │   │   ├── Chrome-extension.mdx
│   │   │   ├── _meta.js
│   │   │   ├── api-key-guide.mdx
│   │   │   ├── chat-widget.mdx
│   │   │   └── search-widget.mdx
│   │   ├── Guides/
│   │   │   ├── Architecture.mdx
│   │   │   ├── Customising-prompts.mdx
│   │   │   ├── How-to-train-on-other-documentation.mdx
│   │   │   ├── How-to-use-different-LLM.mdx
│   │   │   ├── Integrations/
│   │   │   │   ├── _meta.js
│   │   │   │   └── google-drive-connector.mdx
│   │   │   ├── My-AI-answers-questions-using-external-knowledge.mdx
│   │   │   ├── _meta.js
│   │   │   ├── compression.md
│   │   │   └── ocr.mdx
│   │   ├── Models/
│   │   │   ├── _meta.js
│   │   │   ├── cloud-providers.mdx
│   │   │   ├── embeddings.md
│   │   │   └── local-inference.mdx
│   │   ├── Tools/
│   │   │   ├── _meta.js
│   │   │   ├── api-tool.mdx
│   │   │   ├── basics.mdx
│   │   │   └── creating-a-tool.mdx
│   │   ├── _meta.js
│   │   ├── changelog.mdx
│   │   ├── index.mdx
│   │   └── quickstart.mdx
│   ├── mdx-components.jsx
│   ├── next.config.js
│   ├── package.json
│   ├── public/
│   │   ├── favicons/
│   │   │   └── site.webmanifest
│   │   └── llms.txt
│   └── theme.config.jsx
├── extensions/
│   ├── chatwoot/
│   │   ├── .env_sample
│   │   ├── __init__.py
│   │   └── app.py
│   ├── chrome/
│   │   ├── _locales/
│   │   │   └── en/
│   │   │       └── messages.json
│   │   ├── dist/
│   │   │   └── output.css
│   │   ├── js/
│   │   │   └── jquery/
│   │   │       ├── .gitignore
│   │   │       ├── README.md
│   │   │       ├── bower.json
│   │   │       ├── component.json
│   │   │       ├── composer.json
│   │   │       ├── jquery.js
│   │   │       └── package.json
│   │   ├── manifest.json
│   │   ├── package.json
│   │   ├── popup.html
│   │   ├── popup.js
│   │   ├── src/
│   │   │   └── bg/
│   │   │       └── service-worker.js
│   │   ├── styles.css
│   │   └── tailwind.config.js
│   ├── discord/
│   │   ├── __init__.py
│   │   └── bot.py
│   ├── react-widget/
│   │   ├── .gitignore
│   │   ├── .parcelrc
│   │   ├── README.md
│   │   ├── custom.d.ts
│   │   ├── package.json
│   │   ├── publish.sh
│   │   ├── src/
│   │   │   ├── App.tsx
│   │   │   ├── browser.tsx
│   │   │   ├── components/
│   │   │   │   ├── DocsGPTWidget.tsx
│   │   │   │   └── SearchBar.tsx
│   │   │   ├── index.html
│   │   │   ├── index.ts
│   │   │   ├── main.tsx
│   │   │   ├── requests/
│   │   │   │   ├── searchAPI.ts
│   │   │   │   └── streamingApi.ts
│   │   │   ├── types/
│   │   │   │   └── index.ts
│   │   │   └── utils/
│   │   │       └── helper.ts
│   │   └── tsconfig.json
│   ├── slack-bot/
│   │   ├── .gitignore
│   │   ├── Readme.md
│   │   ├── app.py
│   │   └── requirements.txt
│   └── web-widget/
│       ├── README.md
│       ├── dist/
│       │   ├── chat-widget.js
│       │   └── output.css
│       ├── index.html
│       ├── package.json
│       ├── src/
│       │   ├── html/
│       │   │   └── widget.html
│       │   ├── input.css
│       │   └── js/
│       │       └── script.js
│       └── tailwind.config.js
├── frontend/
│   ├── .husky/
│   │   └── pre-commit
│   ├── .prettierignore
│   ├── Dockerfile
│   ├── components.json
│   ├── eslint.config.js
│   ├── index.html
│   ├── package.json
│   ├── postcss.config.cjs
│   ├── prettier.config.cjs
│   ├── src/
│   │   ├── App.tsx
│   │   ├── Hero.tsx
│   │   ├── Navigation.tsx
│   │   ├── PageNotFound.tsx
│   │   ├── agents/
│   │   │   ├── AgentCard.tsx
│   │   │   ├── AgentLogs.tsx
│   │   │   ├── AgentPreview.tsx
│   │   │   ├── AgentsList.tsx
│   │   │   ├── FolderCard.tsx
│   │   │   ├── NewAgent.tsx
│   │   │   ├── SharedAgent.tsx
│   │   │   ├── SharedAgentCard.tsx
│   │   │   ├── SharedAgentGate.tsx
│   │   │   ├── WorkflowBuilder.tsx
│   │   │   ├── agentPreviewSlice.ts
│   │   │   ├── agents.config.ts
│   │   │   ├── components/
│   │   │   │   └── AgentTypeModal.tsx
│   │   │   ├── hooks/
│   │   │   │   ├── useAgentSearch.ts
│   │   │   │   └── useAgentsFetch.ts
│   │   │   ├── index.tsx
│   │   │   ├── types/
│   │   │   │   ├── index.ts
│   │   │   │   └── workflow.ts
│   │   │   └── workflow/
│   │   │       ├── WorkflowBuilder.tsx
│   │   │       ├── WorkflowPreview.tsx
│   │   │       ├── components/
│   │   │       │   ├── MobileBlocker.tsx
│   │   │       │   └── PromptTextArea.tsx
│   │   │       ├── nodes/
│   │   │       │   ├── BaseNode.tsx
│   │   │       │   ├── ConditionNode.tsx
│   │   │       │   ├── SetStateNode.tsx
│   │   │       │   └── index.tsx
│   │   │       └── workflowPreviewSlice.ts
│   │   ├── api/
│   │   │   ├── client.ts
│   │   │   ├── endpoints.ts
│   │   │   └── services/
│   │   │       ├── conversationService.ts
│   │   │       ├── modelService.ts
│   │   │       └── userService.ts
│   │   ├── components/
│   │   │   ├── Accordion.tsx
│   │   │   ├── ActionButtons.tsx
│   │   │   ├── AgentImage.tsx
│   │   │   ├── ArtifactSidebar.tsx
│   │   │   ├── Avatar.tsx
│   │   │   ├── Chunks.tsx
│   │   │   ├── ConfigFields.tsx
│   │   │   ├── ConnectedStateSkeleton.tsx
│   │   │   ├── ConnectorAuth.tsx
│   │   │   ├── ConnectorTree.tsx
│   │   │   ├── ContextMenu.tsx
│   │   │   ├── CopyButton.tsx
│   │   │   ├── DocumentPagination.tsx
│   │   │   ├── Dropdown.tsx
│   │   │   ├── DropdownMenu.tsx
│   │   │   ├── DropdownModel.tsx
│   │   │   ├── FilePicker.tsx
│   │   │   ├── FileSelectionSkeleton.tsx
│   │   │   ├── FileTree.tsx
│   │   │   ├── FileUpload.tsx
│   │   │   ├── GoogleDrivePicker.tsx
│   │   │   ├── Head.tsx
│   │   │   ├── Help.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── MermaidRenderer.tsx
│   │   │   ├── MessageInput.tsx
│   │   │   ├── MultiSelectPopup.tsx
│   │   │   ├── Notification.tsx
│   │   │   ├── RetryIcon.tsx
│   │   │   ├── SearchableDropdown.tsx
│   │   │   ├── SendArrowIcon.tsx
│   │   │   ├── SettingsBar.tsx
│   │   │   ├── Sidebar.tsx
│   │   │   ├── SkeletonLoader.tsx
│   │   │   ├── SourcesPopup.tsx
│   │   │   ├── Spinner.tsx
│   │   │   ├── Table.tsx
│   │   │   ├── TextToSpeechButton.tsx
│   │   │   ├── ToggleSwitch.tsx
│   │   │   ├── ToolsPopup.tsx
│   │   │   ├── UploadToast.tsx
│   │   │   ├── types/
│   │   │   │   ├── Dropdown.types.ts
│   │   │   │   └── index.ts
│   │   │   └── ui/
│   │   │       ├── alert.tsx
│   │   │       ├── button.tsx
│   │   │       ├── command.tsx
│   │   │       ├── dialog.tsx
│   │   │       ├── input.tsx
│   │   │       ├── label.tsx
│   │   │       ├── multi-select.tsx
│   │   │       ├── popover.tsx
│   │   │       ├── select.tsx
│   │   │       └── sheet.tsx
│   │   ├── constants/
│   │   │   └── fileUpload.ts
│   │   ├── conversation/
│   │   │   ├── Conversation.tsx
│   │   │   ├── ConversationBubble.module.css
│   │   │   ├── ConversationBubble.tsx
│   │   │   ├── ConversationMessages.tsx
│   │   │   ├── ConversationTile.tsx
│   │   │   ├── SharedConversation.tsx
│   │   │   ├── conversationHandlers.ts
│   │   │   ├── conversationModels.ts
│   │   │   ├── conversationSlice.ts
│   │   │   ├── sharedConversationSlice.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── hooks/
│   │   │   ├── index.ts
│   │   │   ├── useDataInitializer.ts
│   │   │   └── useTokenAuth.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   └── utils.ts
│   │   ├── locale/
│   │   │   ├── de.json
│   │   │   ├── en.json
│   │   │   ├── es.json
│   │   │   ├── i18n.ts
│   │   │   ├── jp.json
│   │   │   ├── ru.json
│   │   │   ├── zh-TW.json
│   │   │   └── zh.json
│   │   ├── main.tsx
│   │   ├── modals/
│   │   │   ├── AddActionModal.tsx
│   │   │   ├── AddToolModal.tsx
│   │   │   ├── AgentDetailsModal.tsx
│   │   │   ├── ConfigToolModal.tsx
│   │   │   ├── ConfirmationModal.tsx
│   │   │   ├── DeleteConvModal.tsx
│   │   │   ├── FolderManagementModal.tsx
│   │   │   ├── ImportSpecModal.tsx
│   │   │   ├── JWTModal.tsx
│   │   │   ├── MCPServerModal.tsx
│   │   │   ├── MoveToFolderModal.tsx
│   │   │   ├── ShareConversationModal.tsx
│   │   │   ├── WrapperModal.tsx
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── models/
│   │   │   ├── misc.ts
│   │   │   └── types.ts
│   │   ├── preferences/
│   │   │   ├── PromptsModal.tsx
│   │   │   ├── preferenceApi.ts
│   │   │   ├── preferenceSlice.ts
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── settings/
│   │   │   ├── Analytics.tsx
│   │   │   ├── General.tsx
│   │   │   ├── Logs.tsx
│   │   │   ├── Prompts.tsx
│   │   │   ├── Sources.tsx
│   │   │   ├── ToolConfig.tsx
│   │   │   ├── Tools.tsx
│   │   │   ├── index.tsx
│   │   │   └── types/
│   │   │       └── index.ts
│   │   ├── store.ts
│   │   ├── upload/
│   │   │   ├── Upload.tsx
│   │   │   ├── types/
│   │   │   │   └── ingestor.ts
│   │   │   └── uploadSlice.ts
│   │   ├── utils/
│   │   │   ├── browserUtils.ts
│   │   │   ├── chartUtils.ts
│   │   │   ├── dateTimeUtils.ts
│   │   │   ├── objectUtils.ts
│   │   │   ├── providerUtils.ts
│   │   │   └── stringUtils.ts
│   │   └── vite-env.d.ts
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
├── md-gen.py
├── pytest.ini
├── scripts/
│   ├── migrate_conversation_id_dbref_to_objectid.py
│   └── migrate_to_v1_vectorstore.py
├── setup.ps1
├── setup.sh
└── tests/
    ├── __init__.py
    ├── agents/
    │   ├── __init__.py
    │   ├── test_agent_creator.py
    │   ├── test_base_agent.py
    │   ├── test_classic_agent.py
    │   ├── test_get_artifact.py
    │   ├── test_react_agent.py
    │   ├── test_tool_action_parser.py
    │   ├── test_tool_manager.py
    │   ├── test_workflow_engine.py
    │   └── test_workflow_template.py
    ├── api/
    │   ├── __init__.py
    │   ├── answer/
    │   │   ├── __init__.py
    │   │   ├── routes/
    │   │   │   ├── __init__.py
    │   │   │   ├── test_base.py
    │   │   │   └── test_search.py
    │   │   └── services/
    │   │       ├── __init__.py
    │   │       ├── test_conversation_service.py
    │   │       ├── test_prompt_renderer.py
    │   │       └── test_stream_processor.py
    │   ├── conftest.py
    │   └── user/
    │       ├── attachments/
    │       │   └── test_routes.py
    │       ├── sources/
    │       │   ├── __init__.py
    │       │   ├── test_audio_upload.py
    │       │   └── test_routes.py
    │       ├── test_base.py
    │       └── test_exception_sanitization.py
    ├── conftest.py
    ├── core/
    │   └── test_url_validation.py
    ├── integration/
    │   ├── __init__.py
    │   ├── base.py
    │   ├── run_all.py
    │   ├── test_agents.py
    │   ├── test_analytics.py
    │   ├── test_chat.py
    │   ├── test_connectors.py
    │   ├── test_conversations.py
    │   ├── test_mcp.py
    │   ├── test_misc.py
    │   ├── test_prompts.py
    │   ├── test_sources.py
    │   └── test_tools.py
    ├── llm/
    │   ├── handlers/
    │   │   ├── test_google.py
    │   │   ├── test_handler_creator.py
    │   │   ├── test_llm_handlers.py
    │   │   └── test_openai.py
    │   ├── test_anthropic_llm.py
    │   ├── test_google_llm.py
    │   ├── test_openai_llm.py
    │   └── test_sagemaker.py
    ├── parser/
    │   ├── file/
    │   │   ├── test_audio_parser.py
    │   │   ├── test_docs_parser.py
    │   │   ├── test_embedding_pipeline.py
    │   │   ├── test_epub_parser.py
    │   │   ├── test_html_parser.py
    │   │   ├── test_image_parser.py
    │   │   ├── test_json_parser.py
    │   │   ├── test_markdown_parser.py
    │   │   ├── test_pptx_parser.py
    │   │   ├── test_rst_parser.py
    │   │   └── test_tabular_parser.py
    │   └── remote/
    │       ├── test_crawler_loader.py
    │       ├── test_crawler_markdown.py
    │       ├── test_github_loader.py
    │       ├── test_reddit_loader.py
    │       ├── test_s3_loader.py
    │       ├── test_share_point_loader.py
    │       └── test_web_loader.py
    ├── requirements.txt
    ├── security/
    │   └── test_encryption.py
    ├── storage/
    │   ├── test_local_storage.py
    │   └── test_s3_storage.py
    ├── stt/
    │   ├── test_live_session.py
    │   ├── test_stt_creator.py
    │   └── test_upload_limits.py
    ├── test_agent_token_tracking.py
    ├── test_app.py
    ├── test_attachment_worker_audio.py
    ├── test_cache.py
    ├── test_celery.py
    ├── test_compression_service.py
    ├── test_error.py
    ├── test_integration.py
    ├── test_memory_tool.py
    ├── test_model_validation.py
    ├── test_notes_tool.py
    ├── test_openapi3.yaml
    ├── test_openapi3parser.py
    ├── test_todo_tool.py
    ├── test_token_management.py
    ├── test_usage.py
    ├── test_zip_extraction_security.py
    └── tts/
        ├── test_elevenlabs_tts.py
        ├── test_google_tts.py
        └── test_tts_creator.py
Download .txt
Showing preview only (291K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (3205 symbols across 394 files)

FILE: application/agents/agent_creator.py
  class AgentCreator (line 10) | class AgentCreator:
    method create_agent (line 18) | def create_agent(cls, type, *args, **kwargs):

FILE: application/agents/base.py
  class BaseAgent (line 24) | class BaseAgent(ABC):
    method __init__ (line 25) | def __init__(
    method gen (line 86) | def gen(
    method _gen_inner (line 92) | def _gen_inner(
    method _get_tools (line 97) | def _get_tools(self, api_key: str = None) -> Dict[str, Dict]:
    method _get_user_tools (line 118) | def _get_user_tools(self, user="local"):
    method _build_tool_parameters (line 127) | def _build_tool_parameters(self, action):
    method _prepare_tools (line 142) | def _prepare_tools(self, tools_dict):
    method _execute_tool_action (line 165) | def _execute_tool_action(self, tools_dict, call):
    method _get_truncated_tool_calls (line 336) | def _get_truncated_tool_calls(self):
    method _calculate_current_context_tokens (line 354) | def _calculate_current_context_tokens(self, messages: List[Dict]) -> int:
    method _check_context_limit (line 370) | def _check_context_limit(self, messages: List[Dict]) -> bool:
    method _validate_context_size (line 408) | def _validate_context_size(self, messages: List[Dict]) -> None:
    method _truncate_text_middle (line 437) | def _truncate_text_middle(self, text: str, max_tokens: int) -> str:
    method _build_messages (line 476) | def _build_messages(
    method _truncate_history_to_fit (line 555) | def _truncate_history_to_fit(
    method _llm_gen (line 610) | def _llm_gen(self, messages: List[Dict], log_context: Optional[LogCont...
    method _llm_handler (line 645) | def _llm_handler(
    method _handle_response (line 661) | def _handle_response(self, response, tools_dict, messages, log_context):

FILE: application/agents/classic_agent.py
  class ClassicAgent (line 10) | class ClassicAgent(BaseAgent):
    method _gen_inner (line 13) | def _gen_inner(

FILE: application/agents/react_agent.py
  class ReActAgent (line 25) | class ReActAgent(BaseAgent):
    method __init__ (line 36) | def __init__(self, *args, **kwargs):
    method _gen_inner (line 41) | def _gen_inner(
    method _reset_state (line 74) | def _reset_state(self):
    method _planning_phase (line 79) | def _planning_phase(
    method _execution_phase (line 106) | def _execution_phase(
    method _synthesis_phase (line 144) | def _synthesis_phase(
    method _build_planning_prompt (line 166) | def _build_planning_prompt(self, query: str) -> str:
    method _build_execution_prompt (line 174) | def _build_execution_prompt(self, query: str) -> str:
    method _build_final_answer_prompt (line 188) | def _build_final_answer_prompt(self, query: str) -> str:
    method _extract_content (line 197) | def _extract_content(self, response: Any) -> str:

FILE: application/agents/tools/api_body_serializer.py
  class ContentType (line 11) | class ContentType(str, Enum):
  class RequestBodySerializer (line 22) | class RequestBodySerializer:
    method serialize (line 26) | def serialize(
    method _serialize_json (line 84) | def _serialize_json(body_data: Dict[str, Any]) -> tuple[str, Dict[str,...
    method _serialize_form_urlencoded (line 96) | def _serialize_form_urlencoded(
    method _serialize_form_value (line 129) | def _serialize_form_value(
    method _serialize_multipart_form_data (line 167) | def _serialize_multipart_form_data(
    method _create_multipart_part (line 205) | def _create_multipart_part(
    method _serialize_text_plain (line 249) | def _serialize_text_plain(body_data: Dict[str, Any]) -> tuple[str, Dic...
    method _serialize_xml (line 259) | def _serialize_xml(body_data: Dict[str, Any]) -> tuple[str, Dict[str, ...
    method _serialize_octet_stream (line 265) | def _serialize_octet_stream(
    method _percent_encode (line 282) | def _percent_encode(value: str, safe_chars: str = "") -> str:
    method _dict_to_xml (line 293) | def _dict_to_xml(data: Dict[str, Any], root_name: str = "root") -> str:
    method _escape_xml (line 315) | def _escape_xml(value: str) -> str:

FILE: application/agents/tools/api_tool.py
  class APITool (line 21) | class APITool(Tool):
    method __init__ (line 27) | def __init__(self, config):
    method execute_action (line 36) | def execute_action(self, action_name, **kwargs):
    method _make_api_call (line 48) | def _make_api_call(
    method _parse_response (line 236) | def _parse_response(self, response: requests.Response) -> Any:
    method get_actions_metadata (line 274) | def get_actions_metadata(self):
    method get_config_requirements (line 278) | def get_config_requirements(self):

FILE: application/agents/tools/base.py
  class Tool (line 4) | class Tool(ABC):
    method execute_action (line 6) | def execute_action(self, action_name: str, **kwargs):
    method get_actions_metadata (line 10) | def get_actions_metadata(self):
    method get_config_requirements (line 17) | def get_config_requirements(self):

FILE: application/agents/tools/brave.py
  class BraveSearchTool (line 10) | class BraveSearchTool(Tool):
    method __init__ (line 17) | def __init__(self, config):
    method execute_action (line 22) | def execute_action(self, action_name, **kwargs):
    method _web_search (line 33) | def _web_search(
    method _image_search (line 90) | def _image_search(
    method get_actions_metadata (line 135) | def get_actions_metadata(self):
    method get_config_requirements (line 181) | def get_config_requirements(self):

FILE: application/agents/tools/cryptoprice.py
  class CryptoPriceTool (line 5) | class CryptoPriceTool(Tool):
    method __init__ (line 11) | def __init__(self, config):
    method execute_action (line 14) | def execute_action(self, action_name, **kwargs):
    method _get_price (line 22) | def _get_price(self, symbol, currency):
    method get_actions_metadata (line 51) | def get_actions_metadata(self):
    method get_config_requirements (line 74) | def get_config_requirements(self):

FILE: application/agents/tools/duckduckgo.py
  class DuckDuckGoSearchTool (line 14) | class DuckDuckGoSearchTool(Tool):
    method __init__ (line 20) | def __init__(self, config):
    method _get_ddgs_client (line 24) | def _get_ddgs_client(self):
    method _execute_with_retry (line 29) | def _execute_with_retry(self, operation, operation_name: str) -> Dict[...
    method execute_action (line 58) | def execute_action(self, action_name, **kwargs):
    method _web_search (line 68) | def _web_search(
    method _image_search (line 90) | def _image_search(
    method _news_search (line 112) | def _news_search(
    method get_actions_metadata (line 134) | def get_actions_metadata(self):
    method get_config_requirements (line 208) | def get_config_requirements(self):

FILE: application/agents/tools/mcp_tool.py
  class MCPTool (line 37) | class MCPTool(Tool):
    method __init__ (line 43) | def __init__(self, config: Dict[str, Any], user_id: Optional[str] = No...
    method _resolve_redirect_uri (line 90) | def _resolve_redirect_uri(self, configured_redirect_uri: Optional[str]...
    method _generate_cache_key (line 106) | def _generate_cache_key(self) -> str:
    method _setup_client (line 129) | def _setup_client(self):
    method _create_transport (line 174) | def _create_transport(self):
    method _format_tools (line 214) | def _format_tools(self, tools_response) -> List[Dict]:
    method _execute_with_client (line 241) | async def _execute_with_client(self, operation: str, *args, **kwargs):
    method _run_async_operation (line 275) | def _run_async_operation(self, operation: str, *args, **kwargs):
    method _run_in_new_loop (line 290) | def _run_in_new_loop(self, operation, *args, **kwargs):
    method _map_error (line 300) | def _map_error(self, operation: str, exc: Exception) -> Exception:
    method discover_tools (line 311) | def discover_tools(self) -> List[Dict]:
    method execute_action (line 329) | def execute_action(self, action_name: str, **kwargs) -> Any:
    method _format_result (line 377) | def _format_result(self, result) -> Dict:
    method test_connection (line 397) | def test_connection(self) -> Dict:
    method _test_regular_connection (line 439) | def _test_regular_connection(self) -> Dict:
    method _test_oauth_connection (line 477) | def _test_oauth_connection(self) -> Dict:
    method _start_oauth_task (line 513) | def _start_oauth_task(self) -> Dict:
    method get_actions_metadata (line 525) | def get_actions_metadata(self) -> List[Dict]:
    method get_config_requirements (line 569) | def get_config_requirements(self) -> Dict:
  class DocsGPTOAuth (line 656) | class DocsGPTOAuth(OAuthClientProvider):
    method __init__ (line 661) | def __init__(
    method _process_auth_url (line 714) | def _process_auth_url(self, authorization_url: str) -> tuple[str, str]:
    method redirect_handler (line 729) | async def redirect_handler(self, authorization_url: str) -> None:
    method callback_handler (line 753) | async def callback_handler(self) -> tuple[str, str | None]:
  class NonInteractiveOAuth (line 813) | class NonInteractiveOAuth(DocsGPTOAuth):
    method __init__ (line 820) | def __init__(self, **kwargs):
    method redirect_handler (line 825) | async def redirect_handler(self, authorization_url: str) -> None:
    method callback_handler (line 830) | async def callback_handler(self) -> tuple[str, str | None]:
  class DBTokenStorage (line 836) | class DBTokenStorage(TokenStorage):
    method __init__ (line 837) | def __init__(
    method get_base_url (line 851) | def get_base_url(url: str) -> str:
    method get_db_key (line 855) | def get_db_key(self) -> dict:
    method get_tokens (line 861) | async def get_tokens(self) -> OAuthToken | None:
    method set_tokens (line 871) | async def set_tokens(self, tokens: OAuthToken) -> None:
    method get_client_info (line 880) | async def get_client_info(self) -> OAuthClientInformationFull | None:
    method _serialize_client_info (line 912) | def _serialize_client_info(self, info: dict) -> dict:
    method set_client_info (line 917) | async def set_client_info(self, client_info: OAuthClientInformationFul...
    method clear (line 927) | async def clear(self) -> None:
    method clear_all (line 932) | async def clear_all(cls, db_client) -> None:
  class MCPOAuthManager (line 938) | class MCPOAuthManager:
    method __init__ (line 941) | def __init__(self, redis_client: Redis | None, redis_prefix: str = "mc...
    method handle_oauth_callback (line 945) | def handle_oauth_callback(
    method get_oauth_status (line 977) | def get_oauth_status(self, task_id: str) -> Dict[str, Any]:

FILE: application/agents/tools/memory.py
  class MemoryTool (line 12) | class MemoryTool(Tool):
    method __init__ (line 18) | def __init__(self, tool_config: Optional[Dict[str, Any]] = None, user_...
    method execute_action (line 46) | def execute_action(self, action_name: str, **kwargs: Any) -> str:
    method get_actions_metadata (line 96) | def get_actions_metadata(self) -> List[Dict[str, Any]]:
    method get_config_requirements (line 214) | def get_config_requirements(self) -> Dict[str, Any]:
    method _validate_path (line 221) | def _validate_path(self, path: str) -> Optional[str]:
    method _view (line 267) | def _view(self, path: str, view_range: Optional[List[int]] = None) -> ...
    method _view_directory (line 280) | def _view_directory(self, path: str) -> str:
    method _view_file (line 311) | def _view_file(self, path: str, view_range: Optional[List[int]] = None...
    method _create (line 338) | def _create(self, path: str, file_text: str) -> str:
    method _str_replace (line 360) | def _str_replace(self, path: str, old_str: str, new_str: str) -> str:
    method _insert (line 396) | def _insert(self, path: str, insert_line: int, insert_text: str) -> str:
    method _delete (line 433) | def _delete(self, path: str) -> str:
    method _rename (line 477) | def _rename(self, old_path: str, new_path: str) -> str:

FILE: application/agents/tools/notes.py
  class NotesTool (line 10) | class NotesTool(Tool):
    method __init__ (line 16) | def __init__(self, tool_config: Optional[Dict[str, Any]] = None, user_...
    method execute_action (line 46) | def execute_action(self, action_name: str, **kwargs: Any) -> str:
    method get_actions_metadata (line 78) | def get_actions_metadata(self) -> List[Dict[str, Any]]:
    method get_config_requirements (line 128) | def get_config_requirements(self) -> Dict[str, Any]:
    method get_artifact_id (line 132) | def get_artifact_id(self, action_name: str, **kwargs: Any) -> Optional...
    method _get_note (line 138) | def _get_note(self) -> str:
    method _overwrite_note (line 146) | def _overwrite_note(self, content: str) -> str:
    method _str_replace (line 160) | def _str_replace(self, old_str: str, new_str: str) -> str:
    method _insert (line 187) | def _insert(self, line_number: int, text: str) -> str:
    method _delete_note (line 215) | def _delete_note(self) -> str:

FILE: application/agents/tools/ntfy.py
  class NtfyTool (line 4) | class NtfyTool(Tool):
    method __init__ (line 10) | def __init__(self, config):
    method execute_action (line 20) | def execute_action(self, action_name, **kwargs):
    method _send_message (line 42) | def _send_message(self, server_url, message, topic, title=None, priori...
    method get_actions_metadata (line 77) | def get_actions_metadata(self):
    method get_config_requirements (line 118) | def get_config_requirements(self):

FILE: application/agents/tools/postgres.py
  class PostgresTool (line 10) | class PostgresTool(Tool):
    method __init__ (line 17) | def __init__(self, config):
    method execute_action (line 21) | def execute_action(self, action_name, **kwargs):
    method _execute_sql (line 30) | def _execute_sql(self, sql_query):
    method _get_schema (line 75) | def _get_schema(self, db_name):
    method get_actions_metadata (line 135) | def get_actions_metadata(self):
    method get_config_requirements (line 169) | def get_config_requirements(self):

FILE: application/agents/tools/read_webpage.py
  class ReadWebpageTool (line 6) | class ReadWebpageTool(Tool):
    method __init__ (line 12) | def __init__(self, config=None):
    method execute_action (line 19) | def execute_action(self, action_name: str, **kwargs) -> str:
    method get_actions_metadata (line 57) | def get_actions_metadata(self):
    method get_config_requirements (line 79) | def get_config_requirements(self):

FILE: application/agents/tools/spec_parser.py
  function parse_spec (line 22) | def parse_spec(spec_content: str) -> Tuple[Dict[str, Any], List[Dict[str...
  function _load_spec (line 47) | def _load_spec(content: str) -> Dict[str, Any]:
  function _validate_spec (line 62) | def _validate_spec(spec: Dict[str, Any]) -> None:
  function _extract_metadata (line 77) | def _extract_metadata(spec: Dict[str, Any], is_swagger: bool) -> Dict[st...
  function _get_base_url (line 90) | def _get_base_url(spec: Dict[str, Any], is_swagger: bool) -> str:
  function _extract_actions (line 106) | def _extract_actions(spec: Dict[str, Any], is_swagger: bool) -> List[Dic...
  function _build_action (line 144) | def _build_action(
  function _generate_action_name (line 180) | def _generate_action_name(operation: Dict[str, Any], method: str, path: ...
  function _categorize_parameters (line 193) | def _categorize_parameters(
  function _param_to_property (line 216) | def _param_to_property(param: Dict) -> Dict[str, Any]:
  function _extract_request_body (line 232) | def _extract_request_body(
  function _schema_to_properties (line 279) | def _schema_to_properties(
  function _resolve_ref (line 313) | def _resolve_ref(
  function _traverse_path (line 335) | def _traverse_path(obj: Dict, parts: List[str]) -> Optional[Dict]:

FILE: application/agents/tools/telegram.py
  class TelegramTool (line 10) | class TelegramTool(Tool):
    method __init__ (line 17) | def __init__(self, config):
    method execute_action (line 21) | def execute_action(self, action_name, **kwargs):
    method _send_message (line 30) | def _send_message(self, text, chat_id):
    method _send_image (line 37) | def _send_image(self, image_url, chat_id):
    method get_actions_metadata (line 44) | def get_actions_metadata(self):
    method get_config_requirements (line 86) | def get_config_requirements(self):

FILE: application/agents/tools/todo_list.py
  class TodoListTool (line 10) | class TodoListTool(Tool):
    method __init__ (line 16) | def __init__(self, tool_config: Optional[Dict[str, Any]] = None, user_...
    method execute_action (line 46) | def execute_action(self, action_name: str, **kwargs: Any) -> str:
    method get_actions_metadata (line 84) | def get_actions_metadata(self) -> List[Dict[str, Any]]:
    method get_config_requirements (line 168) | def get_config_requirements(self) -> Dict[str, Any]:
    method get_artifact_id (line 172) | def get_artifact_id(self, action_name: str, **kwargs: Any) -> Optional...
    method _coerce_todo_id (line 178) | def _coerce_todo_id(self, value: Optional[Any]) -> Optional[int]:
    method _get_next_todo_id (line 194) | def _get_next_todo_id(self) -> int:
    method _list (line 212) | def _list(self) -> str:
    method _create (line 231) | def _create(self, title: str) -> str:
    method _get (line 255) | def _get(self, todo_id: Optional[Any]) -> str:
    method _update (line 277) | def _update(self, todo_id: Optional[Any], title: str) -> str:
    method _complete (line 300) | def _complete(self, todo_id: Optional[Any]) -> str:
    method _delete (line 319) | def _delete(self, todo_id: Optional[Any]) -> str:

FILE: application/agents/tools/tool_action_parser.py
  class ToolActionParser (line 7) | class ToolActionParser:
    method __init__ (line 8) | def __init__(self, llm_type):
    method parse_args (line 15) | def parse_args(self, call):
    method _parse_openai_llm (line 19) | def _parse_openai_llm(self, call):
    method _parse_google_llm (line 45) | def _parse_google_llm(self, call):

FILE: application/agents/tools/tool_manager.py
  class ToolManager (line 9) | class ToolManager:
    method __init__ (line 10) | def __init__(self, config):
    method load_tools (line 15) | def load_tools(self):
    method load_tool (line 26) | def load_tool(self, tool_name, tool_config, user_id=None):
    method execute_action (line 36) | def execute_action(self, tool_name, action_name, user_id=None, **kwargs):
    method get_all_actions_metadata (line 45) | def get_all_actions_metadata(self):

FILE: application/agents/workflow_agent.py
  class WorkflowAgent (line 22) | class WorkflowAgent(BaseAgent):
    method __init__ (line 25) | def __init__(
    method gen (line 40) | def gen(
    method _gen_inner (line 45) | def _gen_inner(
    method _load_workflow_graph (line 56) | def _load_workflow_graph(self) -> Optional[WorkflowGraph]:
    method _parse_embedded_workflow (line 63) | def _parse_embedded_workflow(self) -> Optional[WorkflowGraph]:
    method _load_from_database (line 104) | def _load_from_database(self) -> Optional[WorkflowGraph]:
    method _save_workflow_run (line 181) | def _save_workflow_run(self, query: str) -> None:
    method _determine_run_status (line 203) | def _determine_run_status(self) -> ExecutionStatus:
    method _serialize_state (line 211) | def _serialize_state(self, state: Dict[str, Any]) -> Dict[str, Any]:
    method _serialize_state_value (line 217) | def _serialize_state_value(self, value: Any) -> Any:

FILE: application/agents/workflows/cel_evaluator.py
  class CelEvaluationError (line 7) | class CelEvaluationError(Exception):
  function _convert_value (line 11) | def _convert_value(value: Any) -> Any:
  function build_activation (line 31) | def build_activation(state: Dict[str, Any]) -> Dict[str, Any]:
  function evaluate_cel (line 35) | def evaluate_cel(expression: str, state: Dict[str, Any]) -> Any:
  function cel_to_python (line 51) | def cel_to_python(value: Any) -> Any:

FILE: application/agents/workflows/node_agent.py
  class ToolFilterMixin (line 11) | class ToolFilterMixin:
    method _get_user_tools (line 16) | def _get_user_tools(self, user: str = "local") -> Dict[str, Dict[str, ...
    method _get_tools (line 27) | def _get_tools(self, api_key: str = None) -> Dict[str, Dict[str, Any]]:
  class WorkflowNodeClassicAgent (line 39) | class WorkflowNodeClassicAgent(ToolFilterMixin, ClassicAgent):
    method __init__ (line 41) | def __init__(
  class WorkflowNodeReActAgent (line 60) | class WorkflowNodeReActAgent(ToolFilterMixin, ReActAgent):
    method __init__ (line 62) | def __init__(
  class WorkflowNodeAgentFactory (line 81) | class WorkflowNodeAgentFactory:
    method create (line 89) | def create(

FILE: application/agents/workflows/schemas.py
  class NodeType (line 9) | class NodeType(str, Enum):
  class AgentType (line 18) | class AgentType(str, Enum):
  class ExecutionStatus (line 23) | class ExecutionStatus(str, Enum):
  class Position (line 30) | class Position(BaseModel):
  class AgentNodeConfig (line 36) | class AgentNodeConfig(BaseModel):
  class ConditionCase (line 52) | class ConditionCase(BaseModel):
  class ConditionNodeConfig (line 59) | class ConditionNodeConfig(BaseModel):
  class StateOperation (line 65) | class StateOperation(BaseModel):
  class WorkflowEdgeCreate (line 71) | class WorkflowEdgeCreate(BaseModel):
  class WorkflowEdge (line 81) | class WorkflowEdge(WorkflowEdgeCreate):
    method convert_objectid (line 86) | def convert_objectid(cls, v: Any) -> Optional[str]:
    method to_mongo_doc (line 91) | def to_mongo_doc(self) -> Dict[str, Any]:
  class WorkflowNodeCreate (line 102) | class WorkflowNodeCreate(BaseModel):
    method parse_position (line 114) | def parse_position(cls, v: Union[Dict[str, float], Position]) -> Posit...
  class WorkflowNode (line 120) | class WorkflowNode(WorkflowNodeCreate):
    method convert_objectid (line 125) | def convert_objectid(cls, v: Any) -> Optional[str]:
    method to_mongo_doc (line 130) | def to_mongo_doc(self) -> Dict[str, Any]:
  class WorkflowCreate (line 142) | class WorkflowCreate(BaseModel):
  class Workflow (line 149) | class Workflow(WorkflowCreate):
    method convert_objectid (line 156) | def convert_objectid(cls, v: Any) -> Optional[str]:
    method to_mongo_doc (line 161) | def to_mongo_doc(self) -> Dict[str, Any]:
  class WorkflowGraph (line 171) | class WorkflowGraph(BaseModel):
    method get_node_by_id (line 176) | def get_node_by_id(self, node_id: str) -> Optional[WorkflowNode]:
    method get_start_node (line 182) | def get_start_node(self) -> Optional[WorkflowNode]:
    method get_outgoing_edges (line 188) | def get_outgoing_edges(self, node_id: str) -> List[WorkflowEdge]:
  class NodeExecutionLog (line 192) | class NodeExecutionLog(BaseModel):
  class WorkflowRunCreate (line 203) | class WorkflowRunCreate(BaseModel):
  class WorkflowRun (line 208) | class WorkflowRun(BaseModel):
    method convert_objectid (line 221) | def convert_objectid(cls, v: Any) -> Optional[str]:
    method to_mongo_doc (line 226) | def to_mongo_doc(self) -> Dict[str, Any]:

FILE: application/agents/workflows/workflow_engine.py
  class WorkflowEngine (line 39) | class WorkflowEngine:
    method __init__ (line 42) | def __init__(self, graph: WorkflowGraph, agent: "BaseAgent"):
    method execute (line 51) | def execute(
    method _initialize_state (line 131) | def _initialize_state(self, initial_inputs: WorkflowState, query: str)...
    method _create_log_entry (line 136) | def _create_log_entry(self, node: WorkflowNode) -> Dict[str, Any]:
    method _get_next_node_id (line 147) | def _get_next_node_id(self, current_node_id: str) -> Optional[str]:
    method _execute_node (line 163) | def _execute_node(
    method _execute_start_node (line 181) | def _execute_start_node(
    method _execute_note_node (line 186) | def _execute_note_node(
    method _execute_agent_node (line 191) | def _execute_agent_node(
    method _execute_state_node (line 287) | def _execute_state_node(
    method _execute_condition_node (line 298) | def _execute_condition_node(
    method _execute_end_node (line 317) | def _execute_end_node(
    method _parse_structured_output (line 326) | def _parse_structured_output(self, raw_response: str) -> tuple[bool, O...
    method _normalize_node_json_schema (line 339) | def _normalize_node_json_schema(
    method _validate_structured_output (line 351) | def _validate_structured_output(self, schema: Dict[str, Any], output_v...
    method _format_template (line 370) | def _format_template(self, template: str) -> str:
    method _build_template_context (line 380) | def _build_template_context(self) -> Dict[str, Any]:
    method _get_source_template_data (line 421) | def _get_source_template_data(self) -> tuple[Optional[List[Dict[str, A...
    method get_execution_summary (line 443) | def get_execution_summary(self) -> List[NodeExecutionLog]:

FILE: application/api/answer/__init__.py
  function init_answer_routes (line 15) | def init_answer_routes():

FILE: application/api/answer/routes/answer.py
  class AnswerResource (line 17) | class AnswerResource(Resource, BaseAnswerResource):
    method __init__ (line 18) | def __init__(self, *args, **kwargs):
    method post (line 70) | def post(self):

FILE: application/api/answer/routes/base.py
  class BaseAnswerResource (line 28) | class BaseAnswerResource:
    method __init__ (line 31) | def __init__(self):
    method validate_request (line 39) | def validate_request(
    method _prepare_tool_calls_for_logging (line 51) | def _prepare_tool_calls_for_logging(
    method check_usage (line 71) | def check_usage(self, agent_config: Dict) -> Optional[Response]:
    method complete_stream (line 165) | def complete_stream(
    method process_response_stream (line 423) | def process_response_stream(self, stream):
    method error_stream_generate (line 477) | def error_stream_generate(self, err_response):

FILE: application/api/answer/routes/search.py
  class SearchResource (line 18) | class SearchResource(Resource):
    method __init__ (line 21) | def __init__(self, *args, **kwargs):
    method _get_sources_from_api_key (line 42) | def _get_sources_from_api_key(self, api_key: str) -> List[str]:
    method _search_vectorstores (line 77) | def _search_vectorstores(
    method post (line 150) | def post(self):

FILE: application/api/answer/routes/stream.py
  class StreamResource (line 17) | class StreamResource(Resource, BaseAnswerResource):
    method __init__ (line 18) | def __init__(self, *args, **kwargs):
    method post (line 76) | def post(self):

FILE: application/api/answer/services/compression/message_builder.py
  class MessageBuilder (line 10) | class MessageBuilder:
    method build_from_compressed_context (line 14) | def build_from_compressed_context(
    method _append_compression_context (line 86) | def _append_compression_context(
    method rebuild_messages_after_compression (line 126) | def rebuild_messages_after_compression(

FILE: application/api/answer/services/compression/orchestrator.py
  class CompressionOrchestrator (line 22) | class CompressionOrchestrator:
    method __init__ (line 30) | def __init__(
    method compress_if_needed (line 45) | def compress_if_needed(
    method _perform_compression (line 99) | def _perform_compression(
    method compress_mid_execution (line 188) | def compress_mid_execution(

FILE: application/api/answer/services/compression/prompt_builder.py
  class CompressionPromptBuilder (line 10) | class CompressionPromptBuilder:
    method __init__ (line 13) | def __init__(self, version: str = "v1.0"):
    method _load_prompt (line 23) | def _load_prompt(self, version: str) -> str:
    method build_prompt (line 49) | def build_prompt(
    method _format_conversation (line 99) | def _format_conversation(self, queries: List[Dict[str, Any]]) -> str:

FILE: application/api/answer/services/compression/service.py
  class CompressionService (line 20) | class CompressionService:
    method __init__ (line 27) | def __init__(
    method compress_conversation (line 50) | def compress_conversation(
    method compress_and_save (line 149) | def compress_and_save(
    method get_compressed_context (line 186) | def get_compressed_context(
    method _extract_summary (line 248) | def _extract_summary(self, llm_response: str) -> str:
    method _log_tool_call_stats (line 278) | def _log_tool_call_stats(self, queries: List[Dict[str, Any]]) -> None:

FILE: application/api/answer/services/compression/threshold_checker.py
  class CompressionThresholdChecker (line 13) | class CompressionThresholdChecker:
    method __init__ (line 16) | def __init__(self, threshold_percentage: float = None):
    method should_compress (line 28) | def should_compress(
    method check_message_tokens (line 76) | def check_message_tokens(self, messages: list, model_id: str) -> bool:

FILE: application/api/answer/services/compression/token_counter.py
  class TokenCounter (line 12) | class TokenCounter:
    method count_message_tokens (line 16) | def count_message_tokens(messages: List[Dict]) -> int:
    method count_query_tokens (line 39) | def count_query_tokens(
    method count_conversation_tokens (line 77) | def count_conversation_tokens(

FILE: application/api/answer/services/compression/types.py
  class CompressionMetadata (line 9) | class CompressionMetadata:
    method to_dict (line 21) | def to_dict(self) -> Dict[str, Any]:
  class CompressionResult (line 36) | class CompressionResult:
    method success_with_compression (line 47) | def success_with_compression(
    method success_no_compression (line 60) | def success_no_compression(cls, queries: List[Dict]) -> "CompressionRe...
    method failure (line 69) | def failure(cls, error: str) -> "CompressionResult":
    method as_history (line 73) | def as_history(self) -> List[Dict[str, str]]:

FILE: application/api/answer/services/conversation_service.py
  class ConversationService (line 14) | class ConversationService:
    method __init__ (line 15) | def __init__(self):
    method get_conversation (line 21) | def get_conversation(
    method save_conversation (line 46) | def save_conversation(
    method update_compression_metadata (line 189) | def update_compression_metadata(
    method append_compression_message (line 230) | def append_compression_message(
    method get_compression_metadata (line 266) | def get_compression_metadata(

FILE: application/api/answer/services/prompt_renderer.py
  class PromptRenderer (line 11) | class PromptRenderer:
    method __init__ (line 14) | def __init__(self):
    method render_prompt (line 18) | def render_prompt(
    method _uses_template_syntax (line 75) | def _uses_template_syntax(self, prompt_content: str) -> bool:
    method _apply_legacy_substitutions (line 79) | def _apply_legacy_substitutions(
    method validate_template (line 91) | def validate_template(self, prompt_content: str) -> bool:
    method extract_variables (line 95) | def extract_variables(self, prompt_content: str) -> set[str]:

FILE: application/api/answer/services/stream_processor.py
  function get_prompt (line 34) | def get_prompt(prompt_id: str, prompts_collection=None) -> str:
  class StreamProcessor (line 68) | class StreamProcessor:
    method __init__ (line 69) | def __init__(
    method initialize (line 105) | def initialize(self):
    method _load_conversation_history (line 114) | def _load_conversation_history(self):
    method _handle_compression (line 137) | def _handle_compression(self, conversation: Dict[str, Any]):
    method _process_attachments (line 186) | def _process_attachments(self):
    method _get_attachments_content (line 193) | def _get_attachments_content(self, attachment_ids, user_id):
    method _validate_and_set_model (line 214) | def _validate_and_set_model(self):
    method _get_agent_key (line 242) | def _get_agent_key(self, agent_id: Optional[str], user_id: Optional[st...
    method _get_data_from_api_key (line 271) | def _get_data_from_api_key(self, api_key: str) -> Dict[str, Any]:
    method _configure_source (line 319) | def _configure_source(self):
    method _resolve_agent_id (line 353) | def _resolve_agent_id(self) -> Optional[str]:
    method _configure_agent (line 378) | def _configure_agent(self):
    method _configure_retriever (line 471) | def _configure_retriever(self):
    method create_retriever (line 484) | def create_retriever(self):
    method pre_fetch_docs (line 498) | def pre_fetch_docs(self, question: str) -> tuple[Optional[str], Option...
    method pre_fetch_tools (line 533) | def pre_fetch_tools(self) -> Optional[Dict[str, Any]]:
    method _fetch_tool_data (line 593) | def _fetch_tool_data(
    method _get_prompt_content (line 683) | def _get_prompt_content(self) -> Optional[str]:
    method _get_required_tool_actions (line 704) | def _get_required_tool_actions(self) -> Optional[Dict[str, Set[Optiona...
    method _fetch_memory_tool_data (line 729) | def _fetch_memory_tool_data(
    method create_agent (line 751) | def create_agent(

FILE: application/api/connector/routes.py
  function build_callback_redirect (line 44) | def build_callback_redirect(params: dict) -> str:
  class ConnectorAuth (line 55) | class ConnectorAuth(Resource):
    method get (line 57) | def get(self):
  class ConnectorsCallback (line 97) | class ConnectorsCallback(Resource):
    method get (line 99) | def get(self):
  class ConnectorFiles (line 201) | class ConnectorFiles(Resource):
    method post (line 211) | def post(self):
  class ConnectorValidateSession (line 275) | class ConnectorValidateSession(Resource):
    method post (line 278) | def post(self):
  class ConnectorDisconnect (line 337) | class ConnectorDisconnect(Resource):
    method post (line 340) | def post(self):
  class ConnectorSync (line 359) | class ConnectorSync(Resource):
    method post (line 370) | def post(self):
  class ConnectorCallbackStatus (line 468) | class ConnectorCallbackStatus(Resource):
    method get (line 470) | def get(self):

FILE: application/api/internal/routes.py
  function verify_internal_key (line 28) | def verify_internal_key():
  function download_file (line 38) | def download_file():
  function upload_index_files (line 47) | def upload_index_files():

FILE: application/api/user/agents/folders.py
  function _folder_error_response (line 22) | def _folder_error_response(message: str, err: Exception):
  class AgentFolders (line 28) | class AgentFolders(Resource):
    method get (line 30) | def get(self):
    method post (line 61) | def post(self):
  class AgentFolder (line 95) | class AgentFolder(Resource):
    method get (line 97) | def get(self, folder_id):
    method put (line 129) | def put(self, folder_id):
    method delete (line 157) | def delete(self, folder_id):
  class MoveAgentToFolder (line 178) | class MoveAgentToFolder(Resource):
    method post (line 189) | def post(self):
  class BulkMoveAgents (line 224) | class BulkMoveAgents(Resource):
    method post (line 235) | def post(self):

FILE: application/api/user/agents/routes.py
  function normalize_workflow_reference (line 110) | def normalize_workflow_reference(workflow_value):
  function validate_workflow_access (line 138) | def validate_workflow_access(workflow_value, user, required=False):
  function build_agent_document (line 159) | def build_agent_document(
  class GetAgent (line 225) | class GetAgent(Resource):
    method get (line 227) | def get(self):
  class GetAgents (line 301) | class GetAgents(Resource):
    method get (line 303) | def get(self):
  class CreateAgent (line 387) | class CreateAgent(Resource):
    method post (line 456) | def post(self):
  class UpdateAgent (line 611) | class UpdateAgent(Resource):
    method put (line 680) | def put(self, agent_id):
  class DeleteAgent (line 1138) | class DeleteAgent(Resource):
    method delete (line 1140) | def delete(self):
  class PinnedAgents (line 1189) | class PinnedAgents(Resource):
    method get (line 1191) | def get(self):
  class GetTemplateAgents (line 1264) | class GetTemplateAgents(Resource):
    method get (line 1266) | def get(self):
  class AdoptAgent (line 1285) | class AdoptAgent(Resource):
    method post (line 1287) | def post(self):
  class PinAgent (line 1325) | class PinAgent(Resource):
    method post (line 1327) | def post(self):
  class RemoveSharedAgent (line 1368) | class RemoveSharedAgent(Resource):
    method delete (line 1373) | def delete(self):

FILE: application/api/user/agents/sharing.py
  class SharedAgent (line 29) | class SharedAgent(Resource):
    method get (line 36) | def get(self):
  class SharedAgents (line 115) | class SharedAgents(Resource):
    method get (line 117) | def get(self):
  class ShareAgent (line 178) | class ShareAgent(Resource):
    method put (line 194) | def put(self):

FILE: application/api/user/agents/webhooks.py
  class AgentWebhook (line 21) | class AgentWebhook(Resource):
    method get (line 26) | def get(self):
  class AgentWebhookListener (line 67) | class AgentWebhookListener(Resource):
    method _enqueue_webhook_task (line 70) | def _enqueue_webhook_task(self, agent_id_str, payload, source_method):
    method post (line 100) | def post(self, webhook_token, agent, agent_id_str):
    method get (line 117) | def get(self, webhook_token, agent, agent_id_str):

FILE: application/api/user/analytics/routes.py
  class GetMessageAnalytics (line 26) | class GetMessageAnalytics(Resource):
    method post (line 48) | def post(self):
  class GetTokenAnalytics (line 146) | class GetTokenAnalytics(Resource):
    method post (line 168) | def post(self):
  class GetFeedbackAnalytics (line 301) | class GetFeedbackAnalytics(Resource):
    method post (line 323) | def post(self):
  class GetUserLogs (line 462) | class GetUserLogs(Resource):
    method post (line 482) | def post(self):

FILE: application/api/user/attachments/routes.py
  function _resolve_authenticated_user (line 43) | def _resolve_authenticated_user():
  function _get_uploaded_file_size (line 63) | def _get_uploaded_file_size(file) -> int:
  function _is_supported_audio_mimetype (line 74) | def _is_supported_audio_mimetype(mimetype: str) -> bool:
  function _enforce_uploaded_audio_size_limit (line 81) | def _enforce_uploaded_audio_size_limit(file, filename: str) -> None:
  function _get_store_attachment_user_error (line 89) | def _get_store_attachment_user_error(exc: Exception) -> str:
  function _require_live_stt_redis (line 95) | def _require_live_stt_redis():
  function _parse_bool_form_value (line 105) | def _parse_bool_form_value(value: str | None) -> bool:
  class StoreAttachment (line 112) | class StoreAttachment(Resource):
    method post (line 127) | def post(self):
  class SpeechToText (line 240) | class SpeechToText(Resource):
    method post (line 253) | def post(self):
  class LiveSpeechToTextStart (line 323) | class LiveSpeechToTextStart(Resource):
    method post (line 325) | def post(self):
  class LiveSpeechToTextChunk (line 366) | class LiveSpeechToTextChunk(Resource):
    method post (line 386) | def post(self):
  class LiveSpeechToTextFinish (line 553) | class LiveSpeechToTextFinish(Resource):
    method post (line 555) | def post(self):
  class ServeImage (line 612) | class ServeImage(Resource):
    method get (line 614) | def get(self, image_path):
  class TextToSpeech (line 640) | class TextToSpeech(Resource):
    method post (line 652) | def post(self):

FILE: application/api/user/base.py
  function generate_minute_range (line 80) | def generate_minute_range(start_date, end_date):
  function generate_hourly_range (line 88) | def generate_hourly_range(start_date, end_date):
  function generate_date_range (line 96) | def generate_date_range(start_date, end_date):
  function ensure_user_doc (line 104) | def ensure_user_doc(user_id):
  function resolve_tool_details (line 138) | def resolve_tool_details(tool_ids):
  function get_vector_store (line 161) | def get_vector_store(source_id):
  function handle_image_upload (line 179) | def handle_image_upload(
  function require_agent (line 214) | def require_agent(func):

FILE: application/api/user/conversations/routes.py
  class DeleteConversation (line 19) | class DeleteConversation(Resource):
    method post (line 24) | def post(self):
  class DeleteAllConversations (line 46) | class DeleteAllConversations(Resource):
    method get (line 50) | def get(self):
  class GetConversations (line 66) | class GetConversations(Resource):
    method get (line 70) | def get(self):
  class GetSingleConversation (line 108) | class GetSingleConversation(Resource):
    method get (line 113) | def get(self):
  class UpdateConversationName (line 169) | class UpdateConversationName(Resource):
    method post (line 184) | def post(self):
  class SubmitFeedback (line 207) | class SubmitFeedback(Resource):
    method post (line 231) | def post(self):

FILE: application/api/user/models/routes.py
  class ModelsListResource (line 10) | class ModelsListResource(Resource):
    method get (line 11) | def get(self):

FILE: application/api/user/prompts/routes.py
  class CreatePrompt (line 19) | class CreatePrompt(Resource):
    method post (line 32) | def post(self):
  class GetPrompts (line 59) | class GetPrompts(Resource):
    method get (line 61) | def get(self):
  class GetSinglePrompt (line 89) | class GetSinglePrompt(Resource):
    method get (line 91) | def get(self):
  class DeletePrompt (line 132) | class DeletePrompt(Resource):
    method post (line 140) | def post(self):
  class UpdatePrompt (line 159) | class UpdatePrompt(Resource):
    method post (line 173) | def post(self):

FILE: application/api/user/sharing/routes.py
  class ShareConversation (line 26) | class ShareConversation(Resource):
    method post (line 41) | def post(self):
  class GetPubliclySharedConversations (line 209) | class GetPubliclySharedConversations(Resource):
    method get (line 211) | def get(self, identifier: str):

FILE: application/api/user/sources/chunks.py
  class GetChunks (line 17) | class GetChunks(Resource):
    method get (line 28) | def get(self):
  class AddChunk (line 102) | class AddChunk(Resource):
    method post (line 119) | def post(self):
  class DeleteChunk (line 155) | class DeleteChunk(Resource):
    method delete (line 160) | def delete(self):
  class UpdateChunk (line 193) | class UpdateChunk(Resource):
    method put (line 215) | def put(self):

FILE: application/api/user/sources/routes.py
  function _get_provider_from_remote_data (line 24) | def _get_provider_from_remote_data(remote_data):
  class CombinedJson (line 40) | class CombinedJson(Resource):
    method get (line 42) | def get(self):
  class PaginatedSources (line 85) | class PaginatedSources(Resource):
    method get (line 87) | def get(self):
  class DeleteByIds (line 158) | class DeleteByIds(Resource):
    method get (line 163) | def get(self):
  class DeleteOldIndexes (line 180) | class DeleteOldIndexes(Resource):
    method get (line 185) | def get(self):
  class RedirectToSources (line 235) | class RedirectToSources(Resource):
    method get (line 239) | def get(self):
  class ManageSync (line 244) | class ManageSync(Resource):
    method post (line 258) | def post(self):
  class SyncSource (line 293) | class SyncSource(Resource):
    method post (line 301) | def post(self):
  class DirectoryStructure (line 359) | class DirectoryStructure(Resource):
    method get (line 364) | def get(self):

FILE: application/api/user/sources/upload.py
  function _enforce_audio_path_size_limit (line 33) | def _enforce_audio_path_size_limit(file_path: str, filename: str) -> None:
  class UploadFile (line 40) | class UploadFile(Resource):
    method post (line 54) | def post(self):
  class UploadRemote (line 169) | class UploadRemote(Resource):
    method post (line 187) | def post(self):
  class ManageSourceFiles (line 267) | class ManageSourceFiles(Resource):
    method post (line 301) | def post(self):
  class TaskStatus (line 616) | class TaskStatus(Resource):
    method get (line 624) | def get(self):

FILE: application/api/user/tasks.py
  function ingest (line 17) | def ingest(
  function ingest_remote (line 34) | def ingest_remote(self, source_data, job_name, user, loader):
  function reingest_source_task (line 40) | def reingest_source_task(self, source_id, user):
  function schedule_syncs (line 48) | def schedule_syncs(self, frequency):
  function sync_source (line 54) | def sync_source(
  function store_attachment (line 78) | def store_attachment(self, file_info, user):
  function process_agent_webhook (line 84) | def process_agent_webhook(self, agent_id, payload):
  function ingest_connector_task (line 90) | def ingest_connector_task(
  function setup_periodic_tasks (line 124) | def setup_periodic_tasks(sender, **kwargs):
  function mcp_oauth_task (line 140) | def mcp_oauth_task(self, config, user):
  function mcp_oauth_status_task (line 146) | def mcp_oauth_status_task(self, task_id):

FILE: application/api/user/tools/mcp.py
  function _sanitize_mcp_transport (line 29) | def _sanitize_mcp_transport(config):
  function _extract_auth_credentials (line 44) | def _extract_auth_credentials(config):
  class TestMCPServerConfig (line 67) | class TestMCPServerConfig(Resource):
    method post (line 79) | def post(self):
  class MCPServerSave (line 126) | class MCPServerSave(Resource):
    method post (line 147) | def post(self):
  class MCPOAuthCallback (line 291) | class MCPOAuthCallback(Resource):
    method get (line 307) | def get(self):
  class MCPOAuthStatus (line 349) | class MCPOAuthStatus(Resource):
    method get (line 350) | def get(self, task_id):
  class MCPAuthStatus (line 399) | class MCPAuthStatus(Resource):
    method get (line 404) | def get(self):

FILE: application/api/user/tools/routes.py
  function _encrypt_secret_fields (line 18) | def _encrypt_secret_fields(config, config_requirements, user_id):
  function _validate_config (line 34) | def _validate_config(config, config_requirements, has_existing_secrets=F...
  function _merge_secrets_on_update (line 59) | def _merge_secrets_on_update(new_config, existing_config, config_require...
  function transform_actions (line 108) | def transform_actions(actions_metadata):
  class AvailableTools (line 130) | class AvailableTools(Resource):
    method get (line 132) | def get(self):
  class GetTools (line 160) | class GetTools(Resource):
    method get (line 162) | def get(self):
  class CreateTool (line 197) | class CreateTool(Resource):
    method post (line 222) | def post(self):
  class UpdateTool (line 291) | class UpdateTool(Resource):
    method post (line 310) | def post(self):
  class UpdateToolConfig (line 393) | class UpdateToolConfig(Resource):
    method post (line 406) | def post(self):
  class UpdateToolActions (line 463) | class UpdateToolActions(Resource):
    method post (line 478) | def post(self):
  class UpdateToolStatus (line 502) | class UpdateToolStatus(Resource):
    method post (line 515) | def post(self):
  class DeleteTool (line 539) | class DeleteTool(Resource):
    method post (line 547) | def post(self):
  class ParseSpec (line 572) | class ParseSpec(Resource):
    method post (line 576) | def post(self):
  class GetArtifact (line 624) | class GetArtifact(Resource):
    method get (line 626) | def get(self, artifact_id: str):

FILE: application/api/user/utils.py
  function get_user_id (line 19) | def get_user_id() -> Optional[str]:
  function require_auth (line 30) | def require_auth(func: Callable) -> Callable:
  function success_response (line 51) | def success_response(
  function error_response (line 73) | def error_response(message: str, status: int = 400, **kwargs) -> Response:
  function validate_object_id (line 94) | def validate_object_id(
  function validate_pagination (line 118) | def validate_pagination(
  function check_resource_ownership (line 146) | def check_resource_ownership(
  function serialize_object_id (line 180) | def serialize_object_id(
  function serialize_list (line 205) | def serialize_list(items: List[Dict], serializer: Callable[[Dict], Dict]...
  function paginated_response (line 222) | def paginated_response(
  function require_fields (line 272) | def require_fields(required: List[str]) -> Callable:
  function safe_db_operation (line 305) | def safe_db_operation(
  function validate_enum (line 335) | def validate_enum(
  function extract_sort_params (line 360) | def extract_sort_params(

FILE: application/api/user/workflows/routes.py
  function _workflow_error_response (line 33) | def _workflow_error_response(message: str, err: Exception):
  function serialize_workflow (line 38) | def serialize_workflow(w: Dict) -> Dict:
  function serialize_node (line 49) | def serialize_node(n: Dict) -> Dict:
  function serialize_edge (line 61) | def serialize_edge(e: Dict) -> Dict:
  function get_workflow_graph_version (line 72) | def get_workflow_graph_version(workflow: Dict) -> int:
  function fetch_graph_documents (line 82) | def fetch_graph_documents(collection, workflow_id: str, graph_version: i...
  function validate_json_schema_payload (line 98) | def validate_json_schema_payload(
  function normalize_agent_node_json_schemas (line 110) | def normalize_agent_node_json_schemas(nodes: List[Dict]) -> List[Dict]:
  function validate_workflow_structure (line 142) | def validate_workflow_structure(nodes: List[Dict], edges: List[Dict]) ->...
  function _can_reach_end (line 304) | def _can_reach_end(
  function create_workflow_nodes (line 318) | def create_workflow_nodes(
  function create_workflow_edges (line 340) | def create_workflow_edges(
  class WorkflowList (line 362) | class WorkflowList(Resource):
    method post (line 366) | def post(self):
  class WorkflowDetail (line 414) | class WorkflowDetail(Resource):
    method get (line 417) | def get(self, workflow_id: str):
    method put (line 448) | def put(self, workflow_id: str):
    method delete (line 526) | def delete(self, workflow_id: str):

FILE: application/app.py
  function home (line 68) | def home():
  function get_config (line 76) | def get_config():
  function generate_token (line 85) | def generate_token():
  function enforce_stt_request_size_limits (line 96) | def enforce_stt_request_size_limits():
  function authenticate_request (line 113) | def authenticate_request():
  function after_request (line 126) | def after_request(response):

FILE: application/auth.py
  function handle_auth (line 6) | def handle_auth(request, data={}):

FILE: application/cache.py
  function get_redis_instance (line 17) | def get_redis_instance():
  function gen_cache_key (line 36) | def gen_cache_key(messages, model="docgpt", tools=None):
  function gen_cache (line 46) | def gen_cache(func):
  function stream_cache (line 78) | def stream_cache(func):

FILE: application/celery_init.py
  function make_celery (line 6) | def make_celery(app_name=__name__):
  function config_loggers (line 17) | def config_loggers(*args, **kwargs):

FILE: application/core/json_schema_utils.py
  class JsonSchemaValidationError (line 4) | class JsonSchemaValidationError(ValueError):
  function normalize_json_schema_payload (line 8) | def normalize_json_schema_payload(json_schema: Any) -> Optional[Dict[str...

FILE: application/core/logging_config.py
  function setup_logging (line 3) | def setup_logging():

FILE: application/core/model_configs.py
  function create_custom_openai_model (line 212) | def create_custom_openai_model(model_name: str, base_url: str) -> Availa...

FILE: application/core/model_settings.py
  class ModelProvider (line 9) | class ModelProvider(str, Enum):
  class ModelCapabilities (line 25) | class ModelCapabilities:
  class AvailableModel (line 36) | class AvailableModel:
    method to_dict (line 45) | def to_dict(self) -> Dict:
  class ModelRegistry (line 63) | class ModelRegistry:
    method __new__ (line 67) | def __new__(cls):
    method __init__ (line 72) | def __init__(self):
    method get_instance (line 80) | def get_instance(cls) -> "ModelRegistry":
    method _load_models (line 83) | def _load_models(self):
    method _add_openai_models (line 147) | def _add_openai_models(self, settings):
    method _add_azure_openai_models (line 177) | def _add_azure_openai_models(self, settings):
    method _add_anthropic_models (line 188) | def _add_anthropic_models(self, settings):
    method _add_google_models (line 203) | def _add_google_models(self, settings):
    method _add_groq_models (line 218) | def _add_groq_models(self, settings):
    method _add_openrouter_models (line 233) | def _add_openrouter_models(self, settings):
    method _add_docsgpt_models (line 248) | def _add_docsgpt_models(self, settings):
    method _add_huggingface_models (line 262) | def _add_huggingface_models(self, settings):
    method _parse_model_names (line 276) | def _parse_model_names(self, llm_name: str) -> List[str]:
    method get_model (line 285) | def get_model(self, model_id: str) -> Optional[AvailableModel]:
    method get_all_models (line 288) | def get_all_models(self) -> List[AvailableModel]:
    method get_enabled_models (line 291) | def get_enabled_models(self) -> List[AvailableModel]:
    method model_exists (line 294) | def model_exists(self, model_id: str) -> bool:

FILE: application/core/model_utils.py
  function get_api_key_for_provider (line 6) | def get_api_key_for_provider(provider: str) -> Optional[str]:
  function get_all_available_models (line 28) | def get_all_available_models() -> Dict[str, Dict[str, Any]]:
  function validate_model_id (line 34) | def validate_model_id(model_id: str) -> bool:
  function get_model_capabilities (line 40) | def get_model_capabilities(model_id: str) -> Optional[Dict[str, Any]]:
  function get_default_model_id (line 54) | def get_default_model_id() -> str:
  function get_provider_from_model_id (line 60) | def get_provider_from_model_id(model_id: str) -> Optional[str]:
  function get_token_limit (line 69) | def get_token_limit(model_id: str) -> int:
  function get_base_url_for_model (line 83) | def get_base_url_for_model(model_id: str) -> Optional[str]:

FILE: application/core/mongo_db.py
  class MongoDB (line 5) | class MongoDB:
    method get_client (line 9) | def get_client(cls):
    method close_client (line 18) | def close_client(cls):

FILE: application/core/settings.py
  class Settings (line 13) | class Settings(BaseSettings):
    method normalize_api_key (line 191) | def normalize_api_key(cls, v: Optional[str]) -> Optional[str]:

FILE: application/core/url_validation.py
  class SSRFError (line 15) | class SSRFError(Exception):
  function is_private_ip (line 39) | def is_private_ip(ip_str: str) -> bool:
  function is_metadata_ip (line 64) | def is_metadata_ip(ip_str: str) -> bool:
  function resolve_hostname (line 77) | def resolve_hostname(hostname: str) -> Optional[str]:
  function validate_url (line 93) | def validate_url(url: str, allow_localhost: bool = False) -> str:
  function validate_url_safe (line 163) | def validate_url_safe(url: str, allow_localhost: bool = False) -> tuple[...

FILE: application/error.py
  function response_error (line 5) | def response_error(code_status, message=None):
  function bad_request (line 14) | def bad_request(status_code=400, message=''):
  function sanitize_api_error (line 18) | def sanitize_api_error(error) -> str:

FILE: application/llm/anthropic.py
  class AnthropicLLM (line 13) | class AnthropicLLM(BaseLLM):
    method __init__ (line 15) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...
    method _raw_gen (line 31) | def _raw_gen(
    method _raw_gen_stream (line 54) | def _raw_gen_stream(
    method get_supported_attachment_types (line 81) | def get_supported_attachment_types(self):
    method prepare_messages_with_attachments (line 98) | def prepare_messages_with_attachments(self, messages, attachments=None):
    method _get_base64_image (line 174) | def _get_base64_image(self, attachment):

FILE: application/llm/base.py
  class BaseLLM (line 12) | class BaseLLM(ABC):
    method __init__ (line 13) | def __init__(
    method fallback_llm (line 29) | def fallback_llm(self):
    method _remove_null_values (line 53) | def _remove_null_values(args_dict):
    method _execute_with_fallback (line 58) | def _execute_with_fallback(
    method gen (line 92) | def gen(self, model, messages, stream=False, tools=None, *args, **kwar...
    method gen_stream (line 105) | def gen_stream(self, model, messages, stream=True, tools=None, *args, ...
    method _raw_gen (line 119) | def _raw_gen(self, model, messages, stream, tools, *args, **kwargs):
    method _raw_gen_stream (line 123) | def _raw_gen_stream(self, model, messages, stream, *args, **kwargs):
    method supports_tools (line 126) | def supports_tools(self):
    method _supports_tools (line 131) | def _supports_tools(self):
    method supports_structured_output (line 134) | def supports_structured_output(self):
    method _supports_structured_output (line 140) | def _supports_structured_output(self):
    method prepare_structured_output_format (line 143) | def prepare_structured_output_format(self, json_schema):
    method get_supported_attachment_types (line 148) | def get_supported_attachment_types(self):

FILE: application/llm/docsgpt_provider.py
  class DocsGPTAPILLM (line 8) | class DocsGPTAPILLM(OpenAILLM):
    method __init__ (line 9) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...
    method _raw_gen (line 18) | def _raw_gen(
    method _raw_gen_stream (line 40) | def _raw_gen_stream(

FILE: application/llm/google_ai.py
  class GoogleLLM (line 12) | class GoogleLLM(BaseLLM):
    method __init__ (line 13) | def __init__(
    method get_supported_attachment_types (line 23) | def get_supported_attachment_types(self):
    method prepare_messages_with_attachments (line 45) | def prepare_messages_with_attachments(self, messages, attachments=None):
    method _upload_file_to_google (line 105) | def _upload_file_to_google(self, attachment):
    method _clean_messages_google (line 144) | def _clean_messages_google(self, messages):
    method _clean_schema (line 248) | def _clean_schema(self, schema_obj):
    method _clean_tools_format (line 297) | def _clean_tools_format(self, tools_list):
    method _extract_preview_from_message (line 332) | def _extract_preview_from_message(self, message):
    method _summarize_messages_for_log (line 379) | def _summarize_messages_for_log(self, messages, preview_chars=20):
    method _get_text_value (line 391) | def _get_text_value(part):
    method _is_thought_part (line 400) | def _is_thought_part(part):
    method _raw_gen (line 406) | def _raw_gen(
    method _raw_gen_stream (line 444) | def _raw_gen_stream(
    method _supports_tools (line 526) | def _supports_tools(self):
    method _supports_structured_output (line 530) | def _supports_structured_output(self):
    method prepare_structured_output_format (line 534) | def prepare_structured_output_format(self, json_schema):

FILE: application/llm/groq.py
  class GroqLLM (line 7) | class GroqLLM(OpenAILLM):
    method __init__ (line 8) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...

FILE: application/llm/handlers/base.py
  class ToolCall (line 13) | class ToolCall:
    method from_dict (line 23) | def from_dict(cls, data: Dict) -> "ToolCall":
  class LLMResponse (line 34) | class LLMResponse:
    method requires_tool_call (line 43) | def requires_tool_call(self) -> bool:
  class LLMHandler (line 48) | class LLMHandler(ABC):
    method __init__ (line 51) | def __init__(self):
    method parse_response (line 56) | def parse_response(self, response: Any) -> LLMResponse:
    method create_tool_message (line 61) | def create_tool_message(self, tool_call: ToolCall, result: Any) -> Dict:
    method _iterate_stream (line 66) | def _iterate_stream(self, response: Any) -> Generator:
    method process_message_flow (line 70) | def process_message_flow(
    method prepare_messages (line 102) | def prepare_messages(
    method _convert_pdf_to_images (line 178) | def _convert_pdf_to_images(self, attachment: Dict) -> List[Dict]:
    method _append_unsupported_attachments (line 209) | def _append_unsupported_attachments(
    method _prune_messages_minimal (line 244) | def _prune_messages_minimal(self, messages: List[Dict]) -> Optional[Li...
    method _extract_text_from_content (line 266) | def _extract_text_from_content(self, content: Any) -> str:
    method _build_conversation_from_messages (line 286) | def _build_conversation_from_messages(self, messages: List[Dict]) -> O...
    method _rebuild_messages_after_compression (line 388) | def _rebuild_messages_after_compression(
    method _perform_mid_execution_compression (line 413) | def _perform_mid_execution_compression(
    method _perform_in_memory_compression (line 531) | def _perform_in_memory_compression(
    method handle_tool_calls (line 645) | def handle_tool_calls(
    method handle_non_streaming (line 828) | def handle_non_streaming(
    method handle_streaming (line 863) | def handle_streaming(

FILE: application/llm/handlers/google.py
  class GoogleLLMHandler (line 7) | class GoogleLLMHandler(LLMHandler):
    method parse_response (line 10) | def parse_response(self, response: Any) -> LLMResponse:
    method create_tool_message (line 69) | def create_tool_message(self, tool_call: ToolCall, result: Any) -> Dict:
    method _iterate_stream (line 84) | def _iterate_stream(self, response: Any) -> Generator:

FILE: application/llm/handlers/handler_creator.py
  class LLMHandlerCreator (line 6) | class LLMHandlerCreator:
    method create_handler (line 14) | def create_handler(cls, llm_type: str, *args, **kwargs) -> LLMHandler:

FILE: application/llm/handlers/openai.py
  class OpenAILLMHandler (line 6) | class OpenAILLMHandler(LLMHandler):
    method parse_response (line 9) | def parse_response(self, response: Any) -> LLMResponse:
    method create_tool_message (line 39) | def create_tool_message(self, tool_call: ToolCall, result: Any) -> Dict:
    method _iterate_stream (line 54) | def _iterate_stream(self, response: Any) -> Generator:

FILE: application/llm/llama_cpp.py
  class LlamaSingleton (line 6) | class LlamaSingleton:
    method get_instance (line 11) | def get_instance(cls, llm_name):
    method query_model (line 23) | def query_model(cls, llm, prompt, **kwargs):
  class LlamaCpp (line 28) | class LlamaCpp(BaseLLM):
    method __init__ (line 29) | def __init__(
    method _raw_gen (line 42) | def _raw_gen(self, baseself, model, messages, stream=False, **kwargs):
    method _raw_gen_stream (line 51) | def _raw_gen_stream(self, baseself, model, messages, stream=True, **kw...

FILE: application/llm/llm_creator.py
  class LLMCreator (line 17) | class LLMCreator:
    method create_llm (line 33) | def create_llm(

FILE: application/llm/novita.py
  class NovitaLLM (line 7) | class NovitaLLM(OpenAILLM):
    method __init__ (line 8) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...

FILE: application/llm/open_router.py
  class OpenRouterLLM (line 7) | class OpenRouterLLM(OpenAILLM):
    method __init__ (line 8) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...

FILE: application/llm/openai.py
  function _truncate_base64_for_logging (line 12) | def _truncate_base64_for_logging(messages):
  class OpenAILLM (line 63) | class OpenAILLM(BaseLLM):
    method __init__ (line 65) | def __init__(self, api_key=None, user_api_key=None, base_url=None, *ar...
    method _clean_messages_openai (line 86) | def _clean_messages_openai(self, messages):
    method _normalize_reasoning_value (line 155) | def _normalize_reasoning_value(value):
    method _extract_reasoning_text (line 180) | def _extract_reasoning_text(cls, delta):
    method _raw_gen (line 199) | def _raw_gen(
    method _raw_gen_stream (line 235) | def _raw_gen_stream(
    method _supports_tools (line 293) | def _supports_tools(self):
    method _supports_structured_output (line 296) | def _supports_structured_output(self):
    method prepare_structured_output_format (line 299) | def prepare_structured_output_format(self, json_schema):
    method get_supported_attachment_types (line 353) | def get_supported_attachment_types(self):
    method prepare_messages_with_attachments (line 366) | def prepare_messages_with_attachments(self, messages, attachments=None):
    method _get_base64_image (line 453) | def _get_base64_image(self, attachment):
    method _upload_file_to_openai (line 472) | def _upload_file_to_openai(self, attachment):
  class AzureOpenAILLM (line 516) | class AzureOpenAILLM(OpenAILLM):
    method __init__ (line 518) | def __init__(self, api_key, user_api_key, *args, **kwargs):

FILE: application/llm/premai.py
  class PremAILLM (line 5) | class PremAILLM(BaseLLM):
    method __init__ (line 7) | def __init__(self, api_key=None, user_api_key=None, *args, **kwargs):
    method _raw_gen (line 16) | def _raw_gen(self, baseself, model, messages, stream=False, **kwargs):
    method _raw_gen_stream (line 27) | def _raw_gen_stream(self, baseself, model, messages, stream=True, **kw...

FILE: application/llm/sagemaker.py
  class LineIterator (line 7) | class LineIterator:
    method __init__ (line 33) | def __init__(self, stream):
    method __iter__ (line 38) | def __iter__(self):
    method __next__ (line 41) | def __next__(self):
  class SagemakerAPILLM (line 61) | class SagemakerAPILLM(BaseLLM):
    method __init__ (line 63) | def __init__(self, api_key=None, user_api_key=None, *args, **kwargs):
    method _raw_gen (line 79) | def _raw_gen(self, baseself, model, messages, stream=False, tools=None...
    method _raw_gen_stream (line 108) | def _raw_gen_stream(self, baseself, model, messages, stream=True, tool...

FILE: application/logging.py
  class LogContext (line 17) | class LogContext:
    method __init__ (line 18) | def __init__(self, endpoint, activity_id, user, api_key, query):
  function build_stack_data (line 27) | def build_stack_data(
  function log_activity (line 71) | def log_activity() -> Callable:
  function _consume_and_log (line 97) | def _consume_and_log(generator: Generator, context: "LogContext"):
  function _log_to_mongodb (line 126) | def _log_to_mongodb(

FILE: application/parser/chunking.py
  class Chunker (line 9) | class Chunker:
    method __init__ (line 10) | def __init__(
    method separate_header_and_body (line 25) | def separate_header_and_body(self, text: str) -> Tuple[str, str]:
    method split_document (line 37) | def split_document(self, doc: Document) -> List[Document]:
    method classic_chunk (line 62) | def classic_chunk(self, documents: List[Document]) -> List[Document]:
    method chunk (line 87) | def chunk(

FILE: application/parser/connectors/base.py
  class BaseConnectorAuth (line 14) | class BaseConnectorAuth(ABC):
    method get_authorization_url (line 23) | def get_authorization_url(self, state: Optional[str] = None) -> str:
    method exchange_code_for_tokens (line 36) | def exchange_code_for_tokens(self, authorization_code: str) -> Dict[st...
    method refresh_access_token (line 49) | def refresh_access_token(self, refresh_token: str) -> Dict[str, Any]:
    method is_token_expired (line 62) | def is_token_expired(self, token_info: Dict[str, Any]) -> bool:
    method sanitize_token_info (line 74) | def sanitize_token_info(self, token_info: Dict[str, Any], **extra_fiel...
  class BaseConnectorLoader (line 86) | class BaseConnectorLoader(ABC):
    method __init__ (line 95) | def __init__(self, session_token: str):
    method load_data (line 105) | def load_data(self, inputs: Dict[str, Any]) -> List[Document]:
    method download_to_directory (line 123) | def download_to_directory(self, local_dir: str, source_config: Dict[st...

FILE: application/parser/connectors/connector_creator.py
  class ConnectorCreator (line 7) | class ConnectorCreator:
    method create_connector (line 26) | def create_connector(cls, connector_type, *args, **kwargs):
    method create_auth (line 46) | def create_auth(cls, connector_type):
    method get_supported_connectors (line 65) | def get_supported_connectors(cls):
    method is_supported (line 75) | def is_supported(cls, connector_type):

FILE: application/parser/connectors/google_drive/auth.py
  class GoogleDriveAuth (line 14) | class GoogleDriveAuth(BaseConnectorAuth):
    method __init__ (line 23) | def __init__(self):
    method get_authorization_url (line 33) | def get_authorization_url(self, state: Optional[str] = None) -> str:
    method exchange_code_for_tokens (line 62) | def exchange_code_for_tokens(self, authorization_code: str) -> Dict[st...
    method refresh_access_token (line 118) | def refresh_access_token(self, refresh_token: str) -> Dict[str, Any]:
    method create_credentials_from_token_info (line 147) | def create_credentials_from_token_info(self, token_info: Dict[str, Any...
    method build_drive_service (line 168) | def build_drive_service(self, credentials: Credentials):
    method is_token_expired (line 194) | def is_token_expired(self, token_info):
    method get_token_info_from_session (line 210) | def get_token_info_from_session(self, session_token: str) -> Dict[str,...
    method validate_credentials (line 243) | def validate_credentials(self, credentials: Credentials) -> bool:

FILE: application/parser/connectors/google_drive/loader.py
  class GoogleDriveLoader (line 19) | class GoogleDriveLoader(BaseConnectorLoader):
    method __init__ (line 51) | def __init__(self, session_token: str):
    method _process_file (line 68) | def _process_file(self, file_metadata: Dict[str, Any], load_content: b...
    method load_data (line 112) | def load_data(self, inputs: Dict[str, Any]) -> List[Document]:
    method _load_file_by_id (line 172) | def _load_file_by_id(self, file_id: str, load_content: bool = True) ->...
    method _list_items_in_parent (line 204) | def _list_items_in_parent(self, parent_id: str, limit: int = 100, load...
    method _download_file_content (line 272) | def _download_file_content(self, file_id: str, mime_type: str) -> Opti...
    method _download_file_to_directory (line 363) | def _download_file_to_directory(self, file_id: str, local_dir: str) ->...
    method _ensure_service (line 371) | def _ensure_service(self):
    method _download_single_file (line 378) | def _download_single_file(self, file_id: str, local_dir: str) -> bool:
    method _download_folder_recursive (line 413) | def _download_folder_recursive(self, folder_id: str, local_dir: str, r...
    method _get_extension_for_mime_type (line 468) | def _get_extension_for_mime_type(self, mime_type: str) -> str:
    method _download_folder_contents (line 480) | def _download_folder_contents(self, folder_id: str, local_dir: str, re...
    method download_to_directory (line 488) | def download_to_directory(self, local_dir: str, source_config: dict = ...

FILE: application/parser/connectors/share_point/auth.py
  class SharePointAuth (line 13) | class SharePointAuth(BaseConnectorAuth):
    method __init__ (line 27) | def __init__(self):
    method get_authorization_url (line 51) | def get_authorization_url(self, state: Optional[str] = None) -> str:
    method exchange_code_for_tokens (line 56) | def exchange_code_for_tokens(self, authorization_code: str) -> Dict[st...
    method refresh_access_token (line 69) | def refresh_access_token(self, refresh_token: str) -> Dict[str, Any]:
    method get_token_info_from_session (line 78) | def get_token_info_from_session(self, session_token: str) -> Dict[str,...
    method is_token_expired (line 113) | def is_token_expired(self, token_info: Dict[str, Any]) -> bool:
    method sanitize_token_info (line 125) | def sanitize_token_info(self, token_info: Dict[str, Any], **extra_fiel...
    method _allows_shared_content (line 134) | def _allows_shared_content(self, id_token_claims: Dict[str, Any]) -> b...
    method map_token_response (line 139) | def map_token_response(self, result) -> Dict[str, Any]:

FILE: application/parser/connectors/share_point/loader.py
  function _retry_on_auth_failure (line 19) | def _retry_on_auth_failure(func):
  class SharePointLoader (line 40) | class SharePointLoader(BaseConnectorLoader):
    method __init__ (line 66) | def __init__(self, session_token: str):
    method _get_headers (line 80) | def _get_headers(self) -> Dict[str, str]:
    method _ensure_valid_token (line 86) | def _ensure_valid_token(self):
    method _get_item_url (line 99) | def _get_item_url(self, item_ref: str) -> str:
    method _process_file (line 105) | def _process_file(self, file_metadata: Dict[str, Any], load_content: b...
    method load_data (line 147) | def load_data(self, inputs: Dict[str, Any]) -> List[Document]:
    method _load_file_by_id (line 202) | def _load_file_by_id(self, file_id: str, load_content: bool = True) ->...
    method _list_items_in_parent (line 221) | def _list_items_in_parent(self, parent_id: str, limit: int = 100, load...
    method _resolve_mime_type (line 289) | def _resolve_mime_type(self, resource: Dict[str, Any]) -> Tuple[str, b...
    method _get_user_drive_web_url (line 304) | def _get_user_drive_web_url(self) -> Optional[str]:
    method _build_shared_kql_query (line 318) | def _build_shared_kql_query(self, search_query: Optional[str], user_dr...
    method _list_shared_items (line 325) | def _list_shared_items(self, limit: int = 100, load_content: bool = Fa...
    method _download_file_content (line 470) | def _download_file_content(self, file_id: str) -> Optional[str]:
    method _download_single_file (line 490) | def _download_single_file(self, file_id: str, local_dir: str) -> bool:
    method _download_folder_recursive (line 521) | def _download_folder_recursive(self, folder_id: str, local_dir: str, r...
    method _download_folder_contents (line 568) | def _download_folder_contents(self, folder_id: str, local_dir: str, re...
    method _download_file_to_directory (line 576) | def _download_file_to_directory(self, file_id: str, local_dir: str) ->...
    method download_to_directory (line 584) | def download_to_directory(self, local_dir: str, source_config: Dict[st...

FILE: application/parser/embedding_pipeline.py
  function sanitize_content (line 10) | def sanitize_content(content: str) -> str:
  function add_text_to_store_with_retry (line 26) | def add_text_to_store_with_retry(store: Any, doc: Any, source_id: str) -...
  function embed_and_store_documents (line 48) | def embed_and_store_documents(docs: List[Any], folder_name: str, source_...

FILE: application/parser/file/audio_parser.py
  class AudioParser (line 10) | class AudioParser(BaseParser):
    method __init__ (line 11) | def __init__(self, parser_config=None):
    method _init_parser (line 15) | def _init_parser(self) -> Dict:
    method parse_file (line 18) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...
    method get_file_metadata (line 47) | def get_file_metadata(self, file: Path) -> Dict:

FILE: application/parser/file/base.py
  class BaseReader (line 9) | class BaseReader:
    method load_data (line 13) | def load_data(self, *args: Any, **load_kwargs: Any) -> List[Document]:
    method load_langchain_documents (line 16) | def load_langchain_documents(self, **load_kwargs: Any) -> List[LCDocum...

FILE: application/parser/file/base_parser.py
  class BaseParser (line 8) | class BaseParser:
    method __init__ (line 11) | def __init__(self, parser_config: Optional[Dict] = None):
    method init_parser (line 15) | def init_parser(self) -> None:
    method parser_config_set (line 21) | def parser_config_set(self) -> bool:
    method parser_config (line 26) | def parser_config(self) -> Dict:
    method _init_parser (line 33) | def _init_parser(self) -> Dict:
    method parse_file (line 37) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...
    method get_file_metadata (line 40) | def get_file_metadata(self, file: Path) -> Dict:

FILE: application/parser/file/bulk.py
  function _build_audio_parser_mapping (line 24) | def _build_audio_parser_mapping() -> Dict[str, BaseParser]:
  function get_default_file_extractor (line 28) | def get_default_file_extractor(
  class SimpleDirectoryReader (line 114) | class SimpleDirectoryReader(BaseReader):
    method __init__ (line 140) | def __init__(
    method _add_files (line 181) | def _add_files(self, input_dir: Path) -> List[Path]:
    method load_data (line 214) | def load_data(self, concatenate: bool = False) -> List[Document]:
    method build_directory_structure (line 295) | def build_directory_structure(self, base_path):

FILE: application/parser/file/docling_parser.py
  class DoclingParser (line 19) | class DoclingParser(BaseParser):
    method __init__ (line 35) | def __init__(
    method _create_converter (line 63) | def _create_converter(self):
    method _init_parser (line 106) | def _init_parser(self) -> Dict:
    method _get_ocr_options (line 132) | def _get_ocr_options(self):
    method _export_content (line 155) | def _export_content(self, document) -> str:
    method parse_file (line 186) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...
  class DoclingPDFParser (line 219) | class DoclingPDFParser(DoclingParser):
    method __init__ (line 229) | def __init__(
  class DoclingDocxParser (line 247) | class DoclingDocxParser(DoclingParser):
    method __init__ (line 250) | def __init__(self):
  class DoclingPPTXParser (line 254) | class DoclingPPTXParser(DoclingParser):
    method __init__ (line 257) | def __init__(self):
  class DoclingXLSXParser (line 261) | class DoclingXLSXParser(DoclingParser):
    method __init__ (line 264) | def __init__(self):
  class DoclingHTMLParser (line 268) | class DoclingHTMLParser(DoclingParser):
    method __init__ (line 271) | def __init__(self):
  class DoclingImageParser (line 275) | class DoclingImageParser(DoclingParser):
    method __init__ (line 282) | def __init__(
  class DoclingCSVParser (line 298) | class DoclingCSVParser(DoclingParser):
    method __init__ (line 301) | def __init__(self):
  class DoclingMarkdownParser (line 305) | class DoclingMarkdownParser(DoclingParser):
    method __init__ (line 308) | def __init__(self):
  class DoclingAsciiDocParser (line 312) | class DoclingAsciiDocParser(DoclingParser):
    method __init__ (line 315) | def __init__(self):
  class DoclingVTTParser (line 319) | class DoclingVTTParser(DoclingParser):
    method __init__ (line 322) | def __init__(self):
  class DoclingXMLParser (line 326) | class DoclingXMLParser(DoclingParser):
    method __init__ (line 329) | def __init__(self):

FILE: application/parser/file/docs_parser.py
  class PDFParser (line 13) | class PDFParser(BaseParser):
    method _init_parser (line 16) | def _init_parser(self) -> Dict:
    method parse_file (line 20) | def parse_file(self, file: Path, errors: str = "ignore") -> str:
  class DocxParser (line 54) | class DocxParser(BaseParser):
    method _init_parser (line 57) | def _init_parser(self) -> Dict:
    method parse_file (line 61) | def parse_file(self, file: Path, errors: str = "ignore") -> str:

FILE: application/parser/file/epub_parser.py
  class EpubParser (line 12) | class EpubParser(BaseParser):
    method _init_parser (line 15) | def _init_parser(self) -> Dict:
    method parse_file (line 19) | def parse_file(self, file: Path, errors: str = "ignore") -> str:

FILE: application/parser/file/html_parser.py
  class HTMLParser (line 12) | class HTMLParser(BaseParser):
    method _init_parser (line 15) | def _init_parser(self) -> Dict:
    method parse_file (line 19) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...

FILE: application/parser/file/image_parser.py
  class ImageParser (line 14) | class ImageParser(BaseParser):
    method _init_parser (line 17) | def _init_parser(self) -> Dict:
    method parse_file (line 21) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...

FILE: application/parser/file/json_parser.py
  class JSONParser (line 7) | class JSONParser(BaseParser):
    method __init__ (line 27) | def __init__(
    method _init_parser (line 41) | def _init_parser(self) -> Dict:
    method parse_file (line 45) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...

FILE: application/parser/file/markdown_parser.py
  class MarkdownParser (line 14) | class MarkdownParser(BaseParser):
    method __init__ (line 22) | def __init__(
    method tups_chunk_append (line 38) | def tups_chunk_append(self, tups: List[Tuple[Optional[str], str]], cur...
    method markdown_to_tups (line 50) | def markdown_to_tups(self, markdown_text: str) -> List[Tuple[Optional[...
    method remove_images (line 89) | def remove_images(self, content: str) -> str:
    method remove_hyperlinks (line 108) | def remove_hyperlinks(self, content: str) -> str:
    method _init_parser (line 114) | def _init_parser(self) -> Dict:
    method parse_tups (line 118) | def parse_tups(
    method parse_file (line 133) | def parse_file(

FILE: application/parser/file/openapi3_parser.py
  class OpenAPI3Parser (line 11) | class OpenAPI3Parser(BaseParser):
    method init_parser (line 12) | def init_parser(self) -> None:
    method get_base_urls (line 15) | def get_base_urls(self, urls):
    method get_info_from_paths (line 24) | def get_info_from_paths(self, path):
    method parse_file (line 34) | def parse_file(self, file_path):

FILE: application/parser/file/pptx_parser.py
  class PPTXParser (line 9) | class PPTXParser(BaseParser):
    method __init__ (line 21) | def __init__(
    method _init_parser (line 33) | def _init_parser(self) -> Dict:
    method parse_file (line 37) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...

FILE: application/parser/file/rst_parser.py
  class RstParser (line 13) | class RstParser(BaseParser):
    method __init__ (line 21) | def __init__(
    method rst_to_tups (line 44) | def rst_to_tups(self, rst_text: str) -> List[Tuple[Optional[str], str]]:
    method chunk_by_token_count (line 94) | def chunk_by_token_count(self, text: str, max_tokens: int = 100) -> Li...
    method remove_images (line 113) | def remove_images(self, content: str) -> str:
    method remove_hyperlinks (line 118) | def remove_hyperlinks(self, content: str) -> str:
    method remove_directives (line 123) | def remove_directives(self, content: str) -> str:
    method remove_interpreters (line 129) | def remove_interpreters(self, content: str) -> str:
    method remove_table_excess (line 135) | def remove_table_excess(self, content: str) -> str:
    method remove_whitespaces_excess (line 141) | def remove_whitespaces_excess(self, content: List[Tuple[str, Any]]) ->...
    method remove_characters_excess (line 147) | def remove_characters_excess(self, content: List[Tuple[str, Any]]) -> ...
    method _init_parser (line 153) | def _init_parser(self) -> Dict:
    method parse_tups (line 157) | def parse_tups(
    method parse_file (line 189) | def parse_file(

FILE: application/parser/file/tabular_parser.py
  class CSVParser (line 12) | class CSVParser(BaseParser):
    method __init__ (line 22) | def __init__(self, *args: Any, concat_rows: bool = True, **kwargs: Any...
    method _init_parser (line 27) | def _init_parser(self) -> Dict:
    method parse_file (line 31) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...
  class PandasCSVParser (line 53) | class PandasCSVParser(BaseParser):
    method __init__ (line 85) | def __init__(
    method _init_parser (line 105) | def _init_parser(self) -> Dict:
    method parse_file (line 109) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...
  class ExcelParser (line 139) | class ExcelParser(BaseParser):
    method __init__ (line 171) | def __init__(
    method _init_parser (line 191) | def _init_parser(self) -> Dict:
    method parse_file (line 195) | def parse_file(self, file: Path, errors: str = "ignore") -> Union[str,...

FILE: application/parser/remote/base.py
  class BaseRemote (line 9) | class BaseRemote:
    method load_data (line 13) | def load_data(self, *args: Any, **load_kwargs: Any) -> List[Document]:
    method load_langchain_documents (line 16) | def load_langchain_documents(self, **load_kwargs: Any) -> List[LCDocum...

FILE: application/parser/remote/crawler_loader.py
  class CrawlerLoader (line 11) | class CrawlerLoader(BaseRemote):
    method __init__ (line 12) | def __init__(self, limit=10):
    method load_data (line 16) | def load_data(self, inputs):
    method _url_to_virtual_path (line 82) | def _url_to_virtual_path(self, url):

FILE: application/parser/remote/crawler_markdown.py
  class CrawlerLoader (line 12) | class CrawlerLoader(BaseRemote):
    method __init__ (line 13) | def __init__(self, limit=10, allow_subdomains=False):
    method load_data (line 25) | def load_data(self, inputs):
    method _fetch_page (line 92) | def _fetch_page(self, url):
    method _process_html_to_markdown (line 106) | def _process_html_to_markdown(self, html_content, current_url):
    method _extract_links (line 120) | def _extract_links(self, html_content, current_url):
    method _get_base_domain (line 128) | def _get_base_domain(self, url):
    method _filter_links (line 134) | def _filter_links(self, links, base_domain):
    method _url_to_virtual_path (line 159) | def _url_to_virtual_path(self, url):

FILE: application/parser/remote/github_loader.py
  class GitHubLoader (line 10) | class GitHubLoader(BaseRemote):
    method __init__ (line 11) | def __init__(self):
    method is_text_file (line 21) | def is_text_file(self, file_path: str) -> bool:
    method fetch_file_content (line 49) | def fetch_file_content(self, repo_url: str, file_path: str) -> Optiona...
    method _make_request (line 77) | def _make_request(self, url: str, max_retries: int = 3) -> requests.Re...
    method fetch_repo_files (line 119) | def fetch_repo_files(self, repo_url: str, path: str = "") -> List[str]:
    method load_data (line 141) | def load_data(self, repo_url: str) -> List[Document]:

FILE: application/parser/remote/reddit_loader.py
  class RedditPostsLoaderRemote (line 6) | class RedditPostsLoaderRemote(BaseRemote):
    method load_data (line 7) | def load_data(self, inputs):

FILE: application/parser/remote/remote_creator.py
  class RemoteCreator (line 9) | class RemoteCreator:
    method create_loader (line 30) | def create_loader(cls, type, *args, **kwargs):

FILE: application/parser/remote/s3_loader.py
  class S3Loader (line 19) | class S3Loader(BaseRemote):
    method __init__ (line 22) | def __init__(self):
    method _normalize_endpoint_url (line 29) | def _normalize_endpoint_url(self, endpoint_url: str, bucket: str) -> t...
    method _init_client (line 79) | def _init_client(
    method is_text_file (line 123) | def is_text_file(self, file_path: str) -> bool:
    method is_supported_document (line 192) | def is_supported_document(self, file_path: str) -> bool:
    method list_objects (line 214) | def list_objects(self, bucket: str, prefix: str = "") -> List[str]:
    method get_object_content (line 282) | def get_object_content(self, bucket: str, key: str) -> Optional[str]:
    method _process_document (line 324) | def _process_document(self, content: bytes, key: str) -> Optional[str]:
    method load_data (line 356) | def load_data(self, inputs) -> List[Document]:

FILE: application/parser/remote/sitemap_loader.py
  class SitemapLoader (line 8) | class SitemapLoader(BaseRemote):
    method __init__ (line 9) | def __init__(self, limit=20):
    method load_data (line 14) | def load_data(self, inputs):
    method _extract_urls (line 49) | def _extract_urls(self, sitemap_url):
    method _is_sitemap (line 70) | def _is_sitemap(self, response):
    method _parse_sitemap (line 80) | def _parse_sitemap(self, sitemap_content):

FILE: application/parser/remote/telegram.py
  class TelegramChatApiRemote (line 4) | class TelegramChatApiRemote(BaseRemote):
    method _init_parser (line 5) | def _init_parser(self, *args, **load_kwargs):
    method parse_file (line 9) | def parse_file(self, *args, **load_kwargs):

FILE: application/parser/remote/web_loader.py
  class WebLoader (line 19) | class WebLoader(BaseRemote):
    method __init__ (line 20) | def __init__(self):
    method load_data (line 23) | def load_data(self, inputs):

FILE: application/parser/schema/base.py
  class Document (line 9) | class Document(BaseDocument):
    method __post_init__ (line 16) | def __post_init__(self) -> None:
    method get_type (line 22) | def get_type(cls) -> str:
    method to_langchain_format (line 26) | def to_langchain_format(self) -> LCDocument:
    method from_langchain_format (line 32) | def from_langchain_format(cls, doc: LCDocument) -> "Document":

FILE: application/parser/schema/schema.py
  class BaseDocument (line 10) | class BaseDocument(DataClassJsonMixin):
    method get_type (line 28) | def get_type(cls) -> str:
    method get_text (line 31) | def get_text(self) -> str:
    method get_doc_id (line 37) | def get_doc_id(self) -> str:
    method is_doc_id_none (line 44) | def is_doc_id_none(self) -> bool:
    method get_embedding (line 48) | def get_embedding(self) -> List[float]:
    method extra_info_str (line 59) | def extra_info_str(self) -> Optional[str]:

FILE: application/retriever/base.py
  class BaseRetriever (line 4) | class BaseRetriever(ABC):
    method __init__ (line 5) | def __init__(self):
    method search (line 9) | def search(self, *args, **kwargs):

FILE: application/retriever/classic_rag.py
  class ClassicRAG (line 11) | class ClassicRAG(BaseRetriever):
    method __init__ (line 12) | def __init__(
    method _validate_vectorstore_config (line 69) | def _validate_vectorstore_config(self):
    method _rephrase_query (line 83) | def _rephrase_query(self):
    method _get_data (line 113) | def _get_data(self):
    method search (line 198) | def search(self, query: str = ""):

FILE: application/retriever/retriever_creator.py
  class RetrieverCreator (line 4) | class RetrieverCreator:
    method create_retriever (line 11) | def create_retriever(cls, type, *args, **kwargs):

FILE: application/security/encryption.py
  function _derive_key (line 13) | def _derive_key(user_id: str, salt: bytes) -> bytes:
  function encrypt_credentials (line 29) | def encrypt_credentials(credentials: dict, user_id: str) -> str:
  function decrypt_credentials (line 52) | def decrypt_credentials(encrypted_data: str, user_id: str) -> dict:
  function _pad_data (line 76) | def _pad_data(data: bytes) -> bytes:
  function _unpad_data (line 83) | def _unpad_data(data: bytes) -> bytes:

FILE: application/seed/commands.py
  function seed (line 9) | def seed():
  function init (line 16) | def init(force):

FILE: application/seed/seeder.py
  class DatabaseSeeder (line 21) | class DatabaseSeeder:
    method __init__ (line 22) | def __init__(self, db):
    method seed_initial_data (line 31) | def seed_initial_data(self, config_path: str = None, force=False):
    method _seed_from_config (line 48) | def _seed_from_config(self, config: Dict):
    method _handle_source (line 126) | def _handle_source(self, agent_config: Dict) -> Union[ObjectId, None, ...
    method _handle_tools (line 167) | def _handle_tools(self, agent_config: Dict) -> List[ObjectId]:
    method _handle_prompt (line 207) | def _handle_prompt(self, agent_config: Dict) -> Optional[str]:
    method _process_config (line 251) | def _process_config(self, config: Dict) -> Dict:
    method _is_already_seeded (line 266) | def _is_already_seeded(self) -> bool:
    method initialize_from_env (line 271) | def initialize_from_env(cls, worker=None):

FILE: application/storage/base.py
  class BaseStorage (line 7) | class BaseStorage(ABC):
    method save_file (line 11) | def save_file(self, file_data: BinaryIO, path: str, **kwargs) -> dict:
    method get_file (line 28) | def get_file(self, path: str) -> BinaryIO:
    method process_file (line 41) | def process_file(self, path: str, processor_func: Callable, **kwargs):
    method delete_file (line 59) | def delete_file(self, path: str) -> bool:
    method file_exists (line 72) | def file_exists(self, path: str) -> bool:
    method list_files (line 85) | def list_files(self, directory: str) -> List[str]:
    method is_directory (line 98) | def is_directory(self, path: str) -> bool:
    method remove_directory (line 111) | def remove_directory(self, directory: str) -> bool:

FILE: application/storage/local.py
  class LocalStorage (line 9) | class LocalStorage(BaseStorage):
    method __init__ (line 12) | def __init__(self, base_dir: str = None):
    method _get_full_path (line 23) | def _get_full_path(self, path: str) -> str:
    method save_file (line 29) | def save_file(self, file_data: BinaryIO, path: str, **kwargs) -> dict:
    method get_file (line 45) | def get_file(self, path: str) -> BinaryIO:
    method delete_file (line 54) | def delete_file(self, path: str) -> bool:
    method file_exists (line 64) | def file_exists(self, path: str) -> bool:
    method list_files (line 69) | def list_files(self, directory: str) -> List[str]:
    method process_file (line 84) | def process_file(self, path: str, processor_func: Callable, **kwargs):
    method is_directory (line 105) | def is_directory(self, path: str) -> bool:
    method remove_directory (line 118) | def remove_directory(self, directory: str) -> bool:

FILE: application/storage/s3.py
  class S3Storage (line 14) | class S3Storage(BaseStorage):
    method __init__ (line 17) | def __init__(self, bucket_name=None):
    method save_file (line 41) | def save_file(
    method get_file (line 62) | def get_file(self, path: str) -> BinaryIO:
    method delete_file (line 71) | def delete_file(self, path: str) -> bool:
    method file_exists (line 79) | def file_exists(self, path: str) -> bool:
    method list_files (line 87) | def list_files(self, directory: str) -> List[str]:
    method process_file (line 103) | def process_file(self, path: str, processor_func: Callable, **kwargs):
    method is_directory (line 134) | def is_directory(self, path: str) -> bool:
    method remove_directory (line 159) | def remove_directory(self, directory: str) -> bool:

FILE: application/storage/storage_creator.py
  class StorageCreator (line 10) | class StorageCreator:
    method get_storage (line 19) | def get_storage(cls) -> BaseStorage:
    method create_storage (line 27) | def create_storage(cls, type_name: str, *args, **kwargs) -> BaseStorage:

FILE: application/stt/base.py
  class BaseSTT (line 6) | class BaseSTT(ABC):
    method transcribe (line 8) | def transcribe(

FILE: application/stt/faster_whisper_stt.py
  class FasterWhisperSTT (line 7) | class FasterWhisperSTT(BaseSTT):
    method __init__ (line 8) | def __init__(
    method _get_model (line 19) | def _get_model(self):
    method transcribe (line 35) | def transcribe(

FILE: application/stt/live_session.py
  function normalize_transcript_text (line 14) | def normalize_transcript_text(text: str) -> str:
  function join_transcript_parts (line 18) | def join_transcript_parts(*parts: str) -> str:
  function _normalize_word (line 22) | def _normalize_word(word: str) -> str:
  function _split_words (line 27) | def _split_words(text: str) -> list[str]:
  function _common_prefix_length (line 32) | def _common_prefix_length(left_words: list[str], right_words: list[str])...
  function _find_suffix_prefix_overlap (line 42) | def _find_suffix_prefix_overlap(
  function strip_committed_prefix (line 58) | def strip_committed_prefix(committed_text: str, hypothesis_text: str) ->...
  function _calculate_commit_count (line 78) | def _calculate_commit_count(
  function create_live_stt_session (line 104) | def create_live_stt_session(
  function get_live_stt_session_key (line 119) | def get_live_stt_session_key(session_id: str) -> str:
  function save_live_stt_session (line 123) | def save_live_stt_session(redis_client, session_state: Dict[str, object]...
  function load_live_stt_session (line 131) | def load_live_stt_session(redis_client, session_id: str) -> Optional[Dic...
  function delete_live_stt_session (line 140) | def delete_live_stt_session(redis_client, session_id: str) -> None:
  function apply_live_stt_hypothesis (line 144) | def apply_live_stt_hypothesis(
  function get_live_stt_transcript_text (line 190) | def get_live_stt_transcript_text(session_state: Dict[str, object]) -> str:
  function finalize_live_stt_session (line 197) | def finalize_live_stt_session(session_state: Dict[str, object]) -> str:

FILE: application/stt/openai_stt.py
  class OpenAISTT (line 10) | class OpenAISTT(BaseSTT):
    method __init__ (line 11) | def __init__(
    method transcribe (line 22) | def transcribe(
    method _to_dict (line 55) | def _to_dict(value: Any) -> Dict[str, Any]:

FILE: application/stt/stt_creator.py
  class STTCreator (line 6) | class STTCreator:
    method create_stt (line 13) | def create_stt(cls, stt_type, *args, **kwargs) -> BaseSTT:

FILE: application/stt/upload_limits.py
  class AudioFileTooLargeError (line 12) | class AudioFileTooLargeError(ValueError):
  function get_stt_max_file_size_bytes (line 16) | def get_stt_max_file_size_bytes() -> int:
  function build_stt_file_size_limit_message (line 20) | def build_stt_file_size_limit_message() -> str:
  function is_audio_filename (line 24) | def is_audio_filename(filename: str | Path | None) -> bool:
  function enforce_audio_file_size_limit (line 31) | def enforce_audio_file_size_limit(size_bytes: int) -> None:
  function should_reject_stt_request (line 37) | def should_reject_stt_request(path: str, content_length: int | None) -> ...

FILE: application/templates/namespaces.py
  class NamespaceBuilder (line 10) | class NamespaceBuilder(ABC):
    method build (line 14) | def build(self, **kwargs) -> Dict[str, Any]:
    method namespace_name (line 20) | def namespace_name(self) -> str:
  class SystemNamespace (line 25) | class SystemNamespace(NamespaceBuilder):
    method namespace_name (line 29) | def namespace_name(self) -> str:
    method build (line 32) | def build(
  class PassthroughNamespace (line 56) | class PassthroughNamespace(NamespaceBuilder):
    method namespace_name (line 60) | def namespace_name(self) -> str:
    method build (line 63) | def build(
  class SourceNamespace (line 88) | class SourceNamespace(NamespaceBuilder):
    method namespace_name (line 92) | def namespace_name(self) -> str:
    method build (line 95) | def build(
  class ToolsNamespace (line 120) | class ToolsNamespace(NamespaceBuilder):
    method namespace_name (line 124) | def namespace_name(self) -> str:
    method build (line 127) | def build(
  class NamespaceManager (line 154) | class NamespaceManager:
    method __init__ (line 157) | def __init__(self):
    method build_context (line 165) | def build_context(self, **kwargs) -> Dict[str, Any]:
    method get_builder (line 188) | def get_builder(self, namespace_name: str) -> Optional[NamespaceBuilder]:

FILE: application/templates/template_engine.py
  class TemplateRenderError (line 16) | class TemplateRenderError(Exception):
  class TemplateEngine (line 22) | class TemplateEngine:
    method __init__ (line 25) | def __init__(self):
    method render (line 33) | def render(self, template_content: str, context: Dict[str, Any]) -> str:
    method validate_template (line 65) | def validate_template(self, template_content: str) -> bool:
    method extract_variables (line 87) | def extract_variables(self, template_content: str) -> Set[str]:
    method extract_tool_usages (line 109) | def extract_tool_usages(

FILE: application/tts/base.py
  class BaseTTS (line 4) | class BaseTTS(ABC):
    method __init__ (line 5) | def __init__(self):
    method text_to_speech (line 9) | def text_to_speech(self, *args, **kwargs):

FILE: application/tts/elevenlabs.py
  class ElevenlabsTTS (line 7) | class ElevenlabsTTS(BaseTTS):
    method __init__ (line 8) | def __init__(self):
    method text_to_speech (line 16) | def text_to_speech(self, text):

FILE: application/tts/google_tts.py
  class GoogleTTS (line 7) | class GoogleTTS(BaseTTS):
    method __init__ (line 8) | def __init__(self):
    method text_to_speech (line 12) | def text_to_speech(self, text):

FILE: application/tts/tts_creator.py
  class TTSCreator (line 7) | class TTSCreator:
    method create_tts (line 14) | def create_tts(cls, tts_type, *args, **kwargs)-> BaseTTS:

FILE: application/usage.py
  function _serialize_for_token_count (line 16) | def _serialize_for_token_count(value):
  function _count_tokens (line 54) | def _count_tokens(value):
  function _count_prompt_tokens (line 61) | def _count_prompt_tokens(messages, tools=None, usage_attachments=None, *...
  function update_token_usage (line 90) | def update_token_usage(decoded_token, user_api_key, token_usage, agent_i...
  function gen_token_usage (line 114) | def gen_token_usage(func):
  function stream_token_usage (line 139) | def stream_token_usage(func):

FILE: application/utils.py
  function get_encoding (line 24) | def get_encoding():
  function get_gpt_model (line 31) | def get_gpt_model() -> str:
  function safe_filename (line 42) | def safe_filename(filename):
  function num_tokens_from_string (line 57) | def num_tokens_from_string(string: str) -> int:
  function num_tokens_from_object_or_list (line 66) | def num_tokens_from_object_or_list(thing):
  function count_tokens_docs (line 77) | def count_tokens_docs(docs):
  function calculate_doc_token_budget (line 85) | def calculate_doc_token_budget(
  function get_missing_fields (line 94) | def get_missing_fields(data, required_fields):
  function check_required_fields (line 99) | def check_required_fields(data, required_fields):
  function get_field_validation_errors (line 115) | def get_field_validation_errors(data, required_fields):
  function validate_required_fields (line 130) | def validate_required_fields(data, required_fields):
  function get_hash (line 149) | def get_hash(data):
  function limit_chat_history (line 153) | def limit_chat_history(history, max_token_limit=None, model_id="docsgpt-...
  function validate_function_name (line 184) | def validate_function_name(function_name):
  function generate_image_url (line 191) | def generate_image_url(image_path):
  function calculate_compression_threshold (line 206) | def calculate_compression_threshold(
  function convert_pdf_to_images (line 224) | def convert_pdf_to_images(
  function clean_text_for_tts (line 311) | def clean_text_for_tts(text: str) -> str:

FILE: application/vectorstore/base.py
  class RemoteEmbeddings (line 11) | class RemoteEmbeddings:
    method __init__ (line 18) | def __init__(self, api_url: str, model_name: str, api_key: str = None):
    method _embed (line 26) | def _embed(self, inputs):
    method embed_query (line 53) | def embed_query(self, query: str):
    method embed_documents (line 68) | def embed_documents(self, documents: list):
    method __call__ (line 77) | def __call__(self, text):
  function _get_embeddings_wrapper (line 86) | def _get_embeddings_wrapper():
  class EmbeddingsSingleton (line 93) | class EmbeddingsSingleton:
    method get_instance (line 97) | def get_instance(embeddings_name, *args, **kwargs):
    method _create_instance (line 105) | def _create_instance(embeddings_name, *args, **kwargs):
  class BaseVectorStore (line 130) | class BaseVectorStore(ABC):
    method __init__ (line 131) | def __init__(self):
    method search (line 135) | def search(self, *args, **kwargs):
    method add_texts (line 140) | def add_texts(self, texts, metadatas=None, *args, **kwargs):
    method delete_index (line 144) | def delete_index(self, *args, **kwargs):
    method save_local (line 148) | def save_local(self, *args, **kwargs):
    method get_chunks (line 152) | def get_chunks(self, *args, **kwargs):
    method add_chunk (line 156) | def add_chunk(self, text, metadata=None, *args, **kwargs):
    method delete_chunk (line 160) | def delete_chunk(self, chunk_id, *args, **kwargs):
    method is_azure_configured (line 164) | def is_azure_configured(self):
    method _get_embeddings (line 171) | def _get_embeddings(self, embeddings_name, embeddings_key=None):

FILE: application/vectorstore/document_class.py
  class Document (line 1) | class Document(str):
    method __new__ (line 4) | def __new__(cls, page_content: str, metadata: dict):

FILE: application/vectorstore/elasticsearch.py
  class ElasticsearchStore (line 6) | class ElasticsearchStore(BaseVectorStore):
    method __init__ (line 9) | def __init__(self, source_id, embeddings_key, index_name=settings.ELAS...
    method connect_to_elasticsearch (line 31) | def connect_to_elasticsearch(
    method search (line 76) | def search(self, question, k=2, index_name=settings.ELASTIC_INDEX, *ar...
    method _create_index_if_not_exists (line 112) | def _create_index_if_not_exists(
    method index (line 126) | def index(
    method add_texts (line 143) | def add_texts(
    method delete_index (line 205) | def delete_index(self):

FILE: application/vectorstore/embeddings_local.py
  class EmbeddingsWrapper (line 12) | class EmbeddingsWrapper:
    method __init__ (line 13) | def __init__(self, model_name, *args, **kwargs):
    method embed_query (line 36) | def embed_query(self, query: str):
    method embed_documents (line 39) | def embed_documents(self, documents: list):
    method __call__ (line 42) | def __call__(self, text):

FILE: application/vectorstore/faiss.py
  function get_vectorstore (line 13) | def get_vectorstore(path: str) -> str:
  class FaissStore (line 21) | class FaissStore(BaseVectorStore):
    method __init__ (line 22) | def __init__(self, source_id: str, embeddings_key: str, docs_init=None):
    method search (line 64) | def search(self, *args, **kwargs):
    method add_texts (line 67) | def add_texts(self, *args, **kwargs):
    method _save_to_storage (line 70) | def _save_to_storage(self):
    method save_local (line 93) | def save_local(self, path=None):
    method delete_index (line 102) | def delete_index(self, *args, **kwargs):
    method assert_embedding_dimensions (line 105) | def assert_embedding_dimensions(self, embeddings):
    method get_chunks (line 123) | def get_chunks(self):
    method add_chunk (line 135) | def add_chunk(self, text, metadata=None):
    method delete_chunk (line 145) | def delete_chunk(self, chunk_id):

FILE: application/vectorstore/lancedb.py
  class LanceDBVectorStore (line 6) | class LanceDBVectorStore(BaseVectorStore):
    method __init__ (line 9) | def __init__(self, path: str = settings.LANCEDB_PATH,
    method pa (line 23) | def pa(self):
    method lancedb (line 30) | def lancedb(self):
    method lance_db (line 37) | def lance_db(self):
    method table (line 44) | def table(self):
    method ensure_table_exists (line 53) | def ensure_table_exists(self):
    method add_texts (line 67) | def add_texts(self, texts: List[str], metadatas: Optional[List[dict]] ...
    method search (line 83) | def search(self, query: str, k: int = 2, *args, **kwargs):
    method delete_index (line 90) | def delete_index(self):
    method assert_embedding_dimensions (line 95) | def assert_embedding_dimensions(self, embeddings):
    method filter_documents (line 106) | def filter_documents(self, filter_condition: dict) -> List[dict]:

FILE: application/vectorstore/milvus.py
  class MilvusStore (line 9) | class MilvusStore(BaseVectorStore):
    method __init__ (line 10) | def __init__(self, source_id: str = "", embeddings_key: str = "embeddi...
    method search (line 25) | def search(self, question, k=2, *args, **kwargs):
    method add_texts (line 29) | def add_texts(self, texts: List[str], metadatas: Optional[List[dict]],...
    method save_local (line 34) | def save_local(self, *args, **kwargs):
    method delete_index (line 37) | def delete_index(self, *args, **kwargs):

FILE: application/vectorstore/mongodb.py
  class MongoDBVectorStore (line 7) | class MongoDBVectorStore(BaseVectorStore):
    method __init__ (line 8) | def __init__(
    method search (line 38) | def search(self, question, k=2, *args, **kwargs):
    method _insert_texts (line 66) | def _insert_texts(self, texts, metadatas):
    method add_texts (line 79) | def add_texts(
    method delete_index (line 126) | def delete_index(self, *args, **kwargs):
    method get_chunks (line 129) | def get_chunks(self):
    method add_chunk (line 153) | def add_chunk(self, text, metadata=None):
    method delete_chunk (line 168) | def delete_chunk(self, chunk_id):

FILE: application/vectorstore/pgvector.py
  class PGVectorStore (line 8) | class PGVectorStore(BaseVectorStore):
    method __init__ (line 9) | def __init__(
    method _get_connection (line 55) | def _get_connection(self):
    method _ensure_table_exists (line 63) | def _ensure_table_exists(self):
    method search (line 110) | def search(self, question: str, k: int = 2, *args, **kwargs) -> List[D...
    method add_texts (line 145) | def add_texts(
    method delete_index (line 188) | def delete_index(self, *args, **kwargs):
    method save_local (line 205) | def save_local(self, *args, **kwargs):
    method get_chunks (line 209) | def get_chunks(self) -> List[Dict[str, Any]]:
    method add_chunk (line 239) | def add_chunk(self, text: str, metadata: Optional[Dict[str, Any]] = No...
    method delete_chunk (line 278) | def delete_chunk(self, chunk_id: str) -> bool:
    method __del__ (line 298) | def __del__(self):

FILE: application/vectorstore/qdrant.py
  class QdrantStore (line 7) | class QdrantStore(BaseVectorStore):
    method __init__ (line 8) | def __init__(self, source_id: str = "", embeddings_key: str = "embeddi...
    method search (line 68) | def search(self, *args, **kwargs):
    method add_texts (line 71) | def add_texts(self, *args, **kwargs):
    method save_local (line 74) | def save_local(self, *args, **kwargs):
    method delete_index (line 77) | def delete_index(self, *args, **kwargs):
    method get_chunks (line 82) | def get_chunks(self):
    method add_chunk (line 110) | def add_chunk(self, text, metadata=None):
    method delete_chunk (line 127) | def delete_chunk(self, chunk_id):

FILE: application/vectorstore/vector_creator.py
  class VectorCreator (line 9) | class VectorCreator:
    method create_vectorstore (line 20) | def create_vectorstore(cls, type, *args, **kwargs):

FILE: application/worker.py
  function metadata_from_filename (line 52) | def metadata_from_filename(title):
  function _normalize_file_name_map (line 56) | def _normalize_file_name_map(file_name_map):
  function _get_display_name (line 67) | def _get_display_name(file_name_map, rel_path):
  function _apply_display_names_to_structure (line 76) | def _apply_display_names_to_structure(structure, file_name_map, prefix=""):
  function generate_random_string (line 94) | def generate_random_string(length):
  class ZipExtractionError (line 108) | class ZipExtractionError(Exception):
  function _is_path_safe (line 113) | def _is_path_safe(base_path: str, target_path: str) -> bool:
  function _validate_zip_safety (line 130) | def _validate_zip_safety(zip_path: str, extract_to: str) -> None:
  function extract_zip_recursive (line 197) | def extract_zip_recursive(zip_path, extract_to, current_depth=0, max_dep...
  function download_file (line 248) | def download_file(url, params, dest_path):
  function upload_index (line 259) | def upload_index(full_path, file_data):
  function run_agent_logic (line 304) | def run_agent_logic(agent_config, input_data):
  function ingest_worker (line 416) | def ingest_worker(
  function reingest_source_worker (line 588) | def reingest_source_worker(self, source_id, user):
  function remote_worker (line 901) | def remote_worker(
  function sync (line 1049) | def sync(
  function sync_worker (line 1079) | def sync_worker(self, frequency):
  function attachment_worker (line 1103) | def attachment_worker(self, file_info, user):
  function agent_webhook_worker (line 1197) | def agent_webhook_worker(self, agent_id, payload):
  function ingest_connector (line 1238) | def ingest_connector(
  function mcp_oauth (line 1426) | def mcp_oauth(self, config: Dict[str, Any], user_id: str = None) -> Dict...
  function mcp_oauth_status (line 1510) | def mcp_oauth_status(self, task_id: str) -> Dict[str, Any]:

FILE: docs/app/[[...mdxPath]]/page.jsx
  function generateMetadata (line 7) | async function generateMetadata(props) {
  function Page (line 15) | async function Page(props) {

FILE: docs/app/layout.jsx
  function RootLayout (line 52) | async function RootLayout({ children }) {

FILE: docs/components/DeploymentCards.jsx
  function DeploymentCards (line 14) | function DeploymentCards({ items }) {

FILE: docs/components/ToolCards.jsx
  function ToolCards (line 16) | function ToolCards({ items }) {

FILE: docs/mdx-components.jsx
  function useMDXComponents (line 3) | function useMDXComponents(components) {

FILE: extensions/chatwoot/app.py
  function send_to_bot (line 18) | def send_to_bot(sender, message):
  function send_to_chatwoot (line 34) | def send_to_chatwoot(account, conversation, message):
  function docsgpt (line 52) | def docsgpt():

FILE: extensions/chrome/js/jquery/jquery.js
  function isArraylike (line 848) | function isArraylike( obj ) {
  function isNative (line 1043) | function isNative( fn ) {
  function createCache (line 1053) | function createCache() {
  function markFunction (line 1071) | function markFunction( fn ) {
  function assert (line 1080) | function assert( fn ) {
  function Sizzle (line 1096) | function Sizzle( selector, context, results, seed ) {
  function siblingCheck (line 1647) | function siblingCheck( a, b ) {
  function boolHandler (line 1669) | function boolHandler( elem, name, isXML ) {
  function interpolationHandler (line 1680) | function interpolationHandler( elem, name, isXML ) {
  function createInputPseudo (line 1688) | function createInputPseudo( type ) {
  function createButtonPseudo (line 1696) | function createButtonPseudo( type ) {
  function createPositionalPseudo (line 1704) | function createPositionalPseudo( fn ) {
  function tokenize (line 2231) | function tokenize( selector, parseOnly ) {
  function toSelector (line 2298) | function toSelector( tokens ) {
  function addCombinator (line 2308) | function addCombinator( matcher, combinator, base ) {
  function elementMatcher (line 2358) | function elementMatcher( matchers ) {
  function condense (line 2372) | function condense( unmatched, map, filter, context, xml ) {
  function setMatcher (line 2393) | function setMatcher( preFilter, selector, matcher, postFilter, postFinde...
  function matcherFromTokens (line 2486) | function matcherFromTokens( tokens ) {
  function matcherFromGroupMatchers (line 2538) | function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
  function multipleContexts (line 2666) | function multipleContexts( selector, contexts, results ) {
  function select (line 2675) | function select( selector, context, results, seed ) {
  function setFilters (line 2744) | function setFilters() {}
  function createOptions (line 2800) | function createOptions( options ) {
  function Data (line 3262) | function Data() {
  function dataAttr (line 3570) | function dataAttr( elem, key, data ) {
  function returnTrue (line 4247) | function returnTrue() {
  function returnFalse (line 4251) | function returnFalse() {
  function safeActiveElement (line 4255) | function safeActiveElement() {
  function sibling (line 5201) | function sibling( cur, dir ) {
  function winnow (line 5319) | function winnow( elements, qualifier, not ) {
  function manipulationTarget (line 5823) | function manipulationTarget( elem, content ) {
  function disableScript (line 5833) | function disableScript( elem ) {
  function restoreScript (line 5837) | function restoreScript( elem ) {
  function setGlobalEval (line 5850) | function setGlobalEval( elems, refElements ) {
  function cloneCopyEvent (line 5861) | function cloneCopyEvent( src, dest ) {
  function getAll (line 5898) | function getAll( context, tag ) {
  function fixInput (line 5909) | function fixInput( src, dest ) {
  function vendorPropName (line 6010) | function vendorPropName( style, name ) {
  function isHidden (line 6032) | function isHidden( elem, el ) {
  function getStyles (line 6041) | function getStyles( elem ) {
  function showHide (line 6045) | function showHide( elements, show ) {
  function setPositiveNumber (line 6313) | function setPositiveNumber( elem, value, subtract ) {
  function augmentWidthOrHeight (line 6321) | function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
  function getWidthOrHeight (line 6360) | function getWidthOrHeight( elem, name, extra ) {
  function css_defaultDisplay (line 6404) | function css_defaultDisplay( nodeName ) {
  function actualDisplay (line 6436) | function actualDisplay( name, doc ) {
  function buildParams (line 6622) | function buildParams( prefix, obj, traditional, add ) {
  function addToPrefiltersOrTransports (line 6738) | function addToPrefiltersOrTransports( structure ) {
  function inspectPrefiltersOrTransports (line 6770) | function inspectPrefiltersOrTransports( structure, options, originalOpti...
  function ajaxExtend (line 6797) | function ajaxExtend( target, src ) {
  function done (line 7243) | function done( status, nativeStatusText, responses, headers ) {
  function ajaxHandleResponses (line 7390) | function ajaxHandleResponses( s, jqXHR, responses ) {
  function ajaxConvert (line 7446) | function ajaxConvert( s, response, jqXHR, isSuccess ) {
  function createFxNow (line 7834) | function createFxNow() {
  function createTweens (line 7841) | function createTweens( animation, props ) {
  function Animation (line 7856) | function Animation( elem, properties, options ) {
  function propFilter (line 7960) | function propFilter( props, specialEasing ) {
  function defaultPrefilter (line 8027) | function defaultPrefilter( elem, props, opts ) {
  function Tween (line 8151) | function Tween( elem, options, prop, end, easing ) {
  function genFx (line 8377) | function genFx( type, includeWidth ) {
  function getWindow (line 8675) | function getWindow( elem ) {

FILE: extensions/discord/bot.py
  function chunk_string (line 30) | def chunk_string(text, max_length=2000):
  function escape_markdown (line 51) | def escape_markdown(text):
  function split_string (line 56) | def split_string(input_str):
  function on_ready (line 66) | async def on_ready():
  function generate_answer (line 69) | async def generate_answer(question, messages, conversation_id):
  function start (line 92) | async def start(ctx):
  function custom_help_command (line 97) | async def custom_help_command(ctx):
  function on_message (line 108) | async def on_message(message):

FILE: extensions/react-widget/src/components/DocsGPTWidget.tsx
  function handleFeedback (line 774) | async function handleFeedback(feedback: FEEDBACK, index: number) {
  function stream (line 831) | async function stream(question: string) {

FILE: extensions/react-widget/src/requests/searchAPI.ts
  function getSearchResults (line 3) | async function getSearchResults(question: string, apiKey: string, apiHos...

FILE: extensions/react-widget/src/requests/streamingApi.ts
  type HistoryItem (line 3) | interface HistoryItem {
  type FetchAnswerStreamingProps (line 8) | interface FetchAnswerStreamingProps {
  type FeedbackPayload (line 18) | interface FeedbackPayload {
  function fetchAnswerStreaming (line 27) | function fetchAnswerStreaming({

FILE: extensions/react-widget/src/types/index.ts
  type MESSAGE_TYPE (line 1) | type MESSAGE_TYPE = 'QUESTION' | 'ANSWER' | 'ERROR';
  type Status (line 3) | type Status = 'idle' | 'loading' | 'failed';
  type FEEDBACK (line 5) | type FEEDBACK = 'LIKE' | 'DISLIKE';
  type THEME (line 7) | type THEME = 'light' | 'dark';
  type Query (line 9) | interface Query {
  type WidgetProps (line 19) | interface WidgetProps {
  type WidgetCoreProps (line 43) | interface WidgetCoreProps extends WidgetProps {
  type SearchBarProps (line 50) | interface SearchBarProps {
  type Result (line 59) | interface Result {

FILE: extensions/react-widget/src/utils/helper.ts
  type ParsedElement (line 28) | interface ParsedElement {

FILE: extensions/slack-bot/app.py
  function encode_conversation_id (line 23) | def encode_conversation_id(conversation_id: str) -> str:
  function generate_answer (line 38) | async def generate_answer(question: str, messages: list, conversation_id...
  function message_docs (line 63) | async def message_docs(message, say):
  function convert_to_slack_markdown (line 84) | def convert_to_slack_markdown(markdown_text: str):
  function main (line 105) | async def main():

FILE: extensions/web-widget/src/js/script.js
  constant API_ENDPOINT (line 1) | const API_ENDPOINT = "http://localhost:7091/api/answer";
  function sendMessage (line 11) | async function sendMessage(message) {

FILE: frontend/src/App.tsx
  function AuthWrapper (line 21) | function AuthWrapper({ children }: { children: React.ReactNode }) {
  function MainLayout (line 35) | function MainLayout() {
  function App (line 56) | function App() {

FILE: frontend/src/Hero.tsx
  function Hero (line 6) | function Hero({

FILE: frontend/src/Navigation.tsx
  type NavigationProps (line 55) | interface NavigationProps {
  function Navigation (line 60) | function Navigation({ navOpen, setNavOpen }: NavigationProps) {

FILE: frontend/src/PageNotFound.tsx
  function PageNotFound (line 4) | function PageNotFound() {

FILE: frontend/src/agents/AgentCard.tsx
  type AgentCardProps (line 29) | type AgentCardProps = {
  function AgentCard (line 36) | function AgentCard({

FILE: frontend/src/agents/AgentLogs.tsx
  function AgentLogs (line 14) | function AgentLogs() {

FILE: frontend/src/agents/AgentPreview.tsx
  function AgentPreview (line 20) | function AgentPreview() {

FILE: frontend/src/agents/AgentsList.tsx
  constant FILTER_TABS (line 28) | const FILTER_TABS: { id: AgentFilterTab; labelKey: string }[] = [
  function AgentsList (line 35) | function AgentsList() {
  type AgentSectionProps (line 242) | interface AgentSectionProps {
  function AgentSection (line 259) | function AgentSection({

FILE: frontend/src/agents/FolderCard.tsx
  type FolderCardProps (line 13) | type FolderCardProps = {
  function FolderCard (line 22) | function FolderCard({

FILE: frontend/src/agents/NewAgent.tsx
  function NewAgent (line 37) | function NewAgent({ mode }: { mode: 'new' | 'edit' | 'draft' }) {
  function AgentPreviewArea (line 1324) | function AgentPreviewArea() {
  function AddPromptModal (line 1345) | function AddPromptModal({

FILE: frontend/src/agents/SharedAgent.tsx
  function SharedAgent (line 27) | function SharedAgent() {

FILE: frontend/src/agents/SharedAgentCard.tsx
  function SharedAgentCard (line 4) | function SharedAgentCard({ agent }: { agent: Agent }) {

FILE: frontend/src/agents/SharedAgentGate.tsx
  function SharedAgentGate (line 3) | function SharedAgentGate() {

FILE: frontend/src/agents/WorkflowBuilder.tsx
  type AgentNodeConfig (line 57) | interface AgentNodeConfig {
  type UserTool (line 72) | interface UserTool {
  function WorkflowBuilderInner (line 78) | function WorkflowBuilderInner() {
  function WorkflowBuilder (line 1147) | function WorkflowBuilder() {

FILE: frontend/src/agents/agentPreviewSlice.ts
  constant API_STREAMING (line 25) | const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
  function handlePreviewAbort (line 28) | function handlePreviewAbort() {
  method addQuery (line 193) | addQuery(state, action: PayloadAction<Query>) {
  method resendQuery (line 196) | resendQuery(
  method updateStreamingQuery (line 214) | updateStreamingQuery(
  method updateThought (line 237) | updateThought(
  method updateStreamingSource (line 250) | updateStreamingSource(
  method updateToolCall (line 264) | updateToolCall(state, action) {
  method updateQuery (line 283) | updateQuery(
  method setStatus (line 293) | setStatus(state, action: PayloadAction<Status>) {
  method raiseError (line 296) | raiseError(
  method extraReducers (line 313) | extraReducers(builder) {
  type RootState (line 332) | type RootState = ReturnType<typeof store.getState>;

FILE: frontend/src/agents/agents.config.ts
  type AgentSectionId (line 11) | type AgentSectionId = 'template' | 'user' | 'shared';

FILE: frontend/src/agents/components/AgentTypeModal.tsx
  type AgentTypeModalProps (line 4) | interface AgentTypeModalProps {
  function AgentTypeModal (line 10) | function AgentTypeModal({

FILE: frontend/src/agents/hooks/useAgentSearch.ts
  type AgentFilterTab (line 12) | type AgentFilterTab = 'all' | AgentSectionId;
  type AgentsBySection (line 14) | type AgentsBySection = Record<AgentSectionId, Agent[]>;
  type UseAgentSearchResult (line 16) | interface UseAgentSearchResult {
  function useAgentSearch (line 43) | function useAgentSearch(): UseAgentSearchResult {

FILE: frontend/src/agents/hooks/useAgentsFetch.ts
  type UseAgentsFetchResult (line 14) | interface UseAgentsFetchResult {
  function useAgentsFetch (line 21) | function useAgentsFetch(): UseAgentsFetchResult {

FILE: frontend/src/agents/index.tsx
  function Agents (line 9) | function Agents() {

FILE: frontend/src/agents/types/index.ts
  type ToolSummary (line 1) | type ToolSummary = {
  type Agent (line 7) | type Agent = {
  type AgentFolder (line 41) | type AgentFolder = {

FILE: frontend/src/agents/types/workflow.ts
  type NodeType (line 1) | type NodeType = 'start' | 'end' | 'agent' | 'note' | 'state' | 'condition';
  type ConditionCase (line 3) | interface ConditionCase {
  type ConditionNodeConfig (line 9) | interface ConditionNodeConfig {
  type StateOperationConfig (line 14) | interface StateOperationConfig {
  type WorkflowEdge (line 19) | interface WorkflowEdge {
  type WorkflowNode (line 27) | interface WorkflowNode {
  type WorkflowDefinition (line 36) | interface WorkflowDefinition {
  type ExecutionStatus (line 45) | type ExecutionStatus = 'pending' | 'running' | 'completed' | 'failed';
  type NodeExecutionLog (line 47) | interface NodeExecutionLog {
  type WorkflowRun (line 58) | interface WorkflowRun {

FILE: frontend/src/agents/workflow/WorkflowBuilder.tsx
  constant PRIMARY_ACTION_SPINNER_DELAY_MS (line 74) | const PRIMARY_ACTION_SPINNER_DELAY_MS = 180;
  type AgentNodeConfig (line 76) | interface AgentNodeConfig {
  type UserTool (line 91) | interface UserTool {
  function validateJsonSchemaConfig (line 97) | function validateJsonSchemaConfig(schema: unknown): string | null {
  function createEmptyWorkflowAgent (line 111) | function createEmptyWorkflowAgent(): Agent {
  function canReachEnd (line 127) | function canReachEnd(
  function parseSimpleCel (line 142) | function parseSimpleCel(expression: string): {
  function buildSimpleCel (line 190) | function buildSimpleCel(
  function normalizeConditionCases (line 215) | function normalizeConditionCases(cases: ConditionCase[]): ConditionCase[] {
  function getNextConditionHandle (line 244) | function getNextConditionHandle(cases: ConditionCase[]): string {
  function createWorkflowPayload (line 261) | function createWorkflowPayload(
  function WorkflowBuilderInner (line 298) | function WorkflowBuilderInner() {
  function WorkflowBuilder (line 2497) | function WorkflowBuilder() {

FILE: frontend/src/agents/workflow/WorkflowPreview.tsx
  type WorkflowData (line 40) | interface WorkflowData {
  type WorkflowPreviewProps (line 47) | interface WorkflowPreviewProps {
  constant NODE_ICONS (line 51) | const NODE_ICONS: Record<string, React.ReactNode> = {
  constant NODE_COLORS (line 60) | const NODE_COLORS: Record<string, string> = {
  function ExecutionDetails (line 69) | function ExecutionDetails({
  function WorkflowMiniMap (line 235) | function WorkflowMiniMap({
  function WorkflowPreview (line 366) | function WorkflowPreview({

FILE: frontend/src/agents/workflow/components/MobileBlocker.tsx
  function MobileBlocker (line 3) | function MobileBlocker() {

FILE: frontend/src/agents/workflow/components/PromptTextArea.tsx
  type WorkflowVariable (line 11) | interface WorkflowVariable {
  constant GLOBAL_CONTEXT_VARIABLES (line 17) | const GLOBAL_CONTEXT_VARIABLES: WorkflowVariable[] = [
  function toAgentTemplatePath (line 65) | function toAgentTemplatePath(variableName: string): string {
  function getUpstreamNodeIds (line 77) | function getUpstreamNodeIds(nodeId: string, edges: Edge[]): Set<string> {
  function extractUpstreamVariables (line 94) | function extractUpstreamVariables(
  function groupBySection (line 171) | function groupBySection(
  function HighlightedOverlay (line 183) | function HighlightedOverlay({ text }: { text: string }) {
  function VariableListWithSearch (line 202) | function VariableListWithSearch({
  type PromptTextAreaProps (line 271) | interface PromptTextAreaProps {
  function PromptTextArea (line 282) | function PromptTextArea({

FILE: frontend/src/agents/workflow/nodes/BaseNode.tsx
  type BaseNodeProps (line 4) | interface BaseNodeProps {

FILE: frontend/src/agents/workflow/nodes/ConditionNode.tsx
  type ConditionNodeData (line 7) | type ConditionNodeData = {
  constant ROW_HEIGHT (line 16) | const ROW_HEIGHT = 18;
  constant HEADER_HEIGHT (line 17) | const HEADER_HEIGHT = 52;
  constant PADDING_BOTTOM (line 18) | const PADDING_BOTTOM = 8;
  function getNodeHeight (line 20) | function getNodeHeight(caseCount: number): number {
  function getHandleTop (line 26) | function getHandleTop(index: number, total: number): string {

FILE: frontend/src/agents/workflow/nodes/SetStateNode.tsx
  type SetStateNodeData (line 8) | type SetStateNodeData = {

FILE: frontend/src/agents/workflow/workflowPreviewSlice.ts
  type WorkflowExecutionStep (line 7) | interface WorkflowExecutionStep {
  type WorkflowData (line 20) | interface WorkflowData {
  type WorkflowQuery (line 27) | interface WorkflowQuery extends Query {
  type WorkflowPreviewState (line 31) | interface WorkflowPreviewState {
  function handleWorkflowPreviewAbort (line 47) | function handleWorkflowPreviewAbort() {
  type ThunkState (line 54) | interface ThunkState {
  method addQuery (line 209) | addQuery(state, action: PayloadAction<Query>) {
  method resendQuery (line 212) | resendQuery(
  method updateStreamingQuery (line 223) | updateStreamingQuery(
  method updateThought (line 246) | updateThought(
  method updateStreamingSource (line 259) | updateStreamingSource(
  method updateToolCall (line 273) | updateToolCall(state, action) {
  method updateQuery (line 294) | updateQuery(
  method updateExecutionStep (line 304) | updateExecutionStep(
  method setActiveNodeId (line 368) | setActiveNodeId(state, action: PayloadAction<string | null>) {
  method setStatus (line 371) | setStatus(state, action: PayloadAction<Status>) {
  method raiseError (line 374) | raiseError(
  method extraReducers (line 396) | extraReducers(builder) {
  type RootStateWithWorkflowPreview (line 417) | interface RootStateWithWorkflowPreview {

FILE: frontend/src/components/Accordion.tsx
  type AccordionProps (line 5) | type AccordionProps = {
  function Accordion (line 14) | function Accordion({

FILE: frontend/src/components/ActionButtons.tsx
  type ActionButtonsProps (line 15) | interface ActionButtonsProps {
  function ActionButtons (line 24) | function ActionButtons({

FILE: frontend/src/components/AgentImage.tsx
  type AgentImageProps (line 4) | type AgentImageProps = {
  function AgentImage (line 11) | function AgentImage({

FILE: frontend/src/components/ArtifactSidebar.tsx
  type TodoItem (line 18) | type TodoItem = {
  type TodoArtifactData (line 26) | type TodoArtifactData = {
  type NoteArtifactData (line 33) | type NoteArtifactData = {
  type ArtifactData (line 39) | type ArtifactData =
  type ArtifactSidebarProps (line 44) | type ArtifactSidebarProps = {
  constant ARTIFACT_TITLE_BY_TYPE (line 57) | const ARTIFACT_TITLE_BY_TYPE: Record<ArtifactData['artifact_type'], stri...
  function getArtifactTitle (line 63) | function getArtifactTitle(artifact: ArtifactData | null, toolName?: stri...
  function TodoListView (line 75) | function TodoListView({ data }: { data: TodoArtifactData }) {
  function NoteView (line 148) | function NoteView({ data }: { data: NoteArtifactData }) {
  function ArtifactSidebar (line 270) | function ArtifactSidebar({

FILE: frontend/src/components/Avatar.tsx
  function Avatar (line 3) | function Avatar({

FILE: frontend/src/components/Chunks.tsx
  type LineNumberedTextareaProps (line 23) | interface LineNumberedTextareaProps {
  type SearchResult (line 92) | interface SearchResult {
  type ChunksProps (line 98) | interface ChunksProps {

FILE: frontend/src/components/ConfigFields.tsx
  type ConfigValues (line 16) | type ConfigValues = { [key: string]: any };
  type ConfigFieldsProps (line 18) | interface ConfigFieldsProps {
  function shouldShowField (line 27) | function shouldShowField(
  function ConfigFields (line 37) | function ConfigFields({

FILE: frontend/src/components/ConnectorAuth.tsx
  type ConnectorAuthProps (line 7) | interface ConnectorAuthProps {

FILE: frontend/src/components/ConnectorTree.tsx
  type FileNode (line 30) | interface FileNode {
  type DirectoryStructure (line 38) | interface DirectoryStructure {
  type ConnectorTreeProps (line 42) | interface ConnectorTreeProps {
  type SearchResult (line 48) | interface SearchResult {

FILE: frontend/src/components/ContextMenu.tsx
  type MenuOption (line 5) | interface MenuOption {
  type ContextMenuProps (line 15) | interface ContextMenuProps {
  function ContextMenu (line 25) | function ContextMenu({

FILE: frontend/src/components/CopyButton.tsx
  type CopyButtonProps (line 9) | type CopyButtonProps = {
  constant DEFAULT_ICON_SIZE (line 20) | const DEFAULT_ICON_SIZE = 'w-4 h-4';
  constant DEFAULT_PADDING (line 21) | const DEFAULT_PADDING = 'p-2';
  constant DEFAULT_COPIED_DURATION (line 22) | const DEFAULT_COPIED_DURATION = 2000;
  function CopyButton (line 24) | function CopyButton({

FILE: frontend/src/components/DocumentPagination.tsx
  type PaginationProps (line 8) | interface PaginationProps {

FILE: frontend/src/components/Dropdown.tsx
  function Dropdown (line 8) | function Dropdown<T extends DropdownOption>({

FILE: frontend/src/components/DropdownMenu.tsx
  type DropdownMenuProps (line 4) | type DropdownMenuProps = {
  function DropdownMenu (line 18) | function DropdownMenu({

FILE: frontend/src/components/DropdownModel.tsx
  function DropdownModel (line 17) | function DropdownModel() {

FILE: frontend/src/components/FilePicker.tsx
  type CloudFile (line 26) | interface CloudFile {
  type CloudFilePickerProps (line 35) | interface CloudFilePickerProps {

FILE: frontend/src/components/FileTree.tsx
  type FileNode (line 29) | interface FileNode {
  type DirectoryStructure (line 37) | interface DirectoryStructure {
  type FileTreeProps (line 41) | interface FileTreeProps {
  type SearchResult (line 47) | interface SearchResult {
  type QueuedOperation (line 88) | type QueuedOperation = {

FILE: frontend/src/components/FileUpload.tsx
  type FileUploadProps (line 9) | interface FileUploadProps {

FILE: frontend/src/components/GoogleDrivePicker.tsx
  type PickerFile (line 15) | interface PickerFile {
  type GoogleDrivePickerProps (line 24) | interface GoogleDrivePickerProps {

FILE: frontend/src/components/Head.tsx
  type HeadProps (line 3) | interface HeadProps {
  function Head (line 16) | function Head({

FILE: frontend/src/components/MessageInput.tsx
  type RecordingState (line 45) | type RecordingState = 'idle' | 'recording' | 'transcribing' | 'error';
  constant LIVE_TRANSCRIPTION_TIMESLICE_MS (line 47) | const LIVE_TRANSCRIPTION_TIMESLICE_MS = 1000;
  constant LIVE_CAPTURE_SAMPLE_RATE (line 48) | const LIVE_CAPTURE_SAMPLE_RATE = 16000;
  constant LIVE_CAPTURE_MAX_BUFFER_SECONDS (line 49) | const LIVE_CAPTURE_MAX_BUFFER_SECONDS = 20;
  constant LIVE_SILENCE_RMS_THRESHOLD (line 50) | const LIVE_SILENCE_RMS_THRESHOLD = 0.015;
  constant ENABLE_VOICE_INPUT (line 51) | const ENABLE_VOICE_INPUT = import.meta.env.VITE_ENABLE_VOICE_INPUT === '...
  type AudioContextWindow (line 53) | type AudioContextWindow = Window &
  type LegacyNavigator (line 58) | type LegacyNavigator = Navigator & {
  type LiveAudioSnapshot (line 76) | type LiveAudioSnapshot = {
  type MessageInputProps (line 285) | type MessageInputProps = {
  function MessageInput (line 293) | function MessageInput({

FILE: frontend/src/components/MultiSelectPopup.tsx
  type OptionType (line 10) | type OptionType = {
  type MultiSelectPopupProps (line 17) | type MultiSelectPopupProps = {
  function MultiSelectPopup (line 33) | function MultiSelectPopup({

FILE: frontend/src/components/Notification.tsx
  type NotificationProps (line 4) | interface NotificationProps {
  function Notification (line 20) | function Notification({

FILE: frontend/src/components/SearchableDropdown.tsx
  type SearchableDropdownOptionBase (line 12) | type SearchableDropdownOptionBase = {
  type NameIdOption (line 17) | type NameIdOption = { name: string; id: string } & SearchableDropdownOpt...
  type SearchableDropdownOption (line 19) | type SearchableDropdownOption =
  type SearchableDropdownSelectedValue (line 25) | type SearchableDropdownSelectedValue = SearchableDropdownOption | null;
  type SearchableDropdownProps (line 27) | interface SearchableDropdownProps<
  function SearchableDropdown (line 44) | function SearchableDropdown<T extends SearchableDropdownOption>({

FILE: frontend/src/components/SettingsBar.tsx
  type HiddenGradientType (line 7) | type HiddenGradientType = 'left' | 'right' | undefined;
  type SettingsBarProps (line 21) | interface SettingsBarProps {

FILE: frontend/src/components/Sidebar.tsx
  type SidebarProps (line 5) | type SidebarProps = {
  function Sidebar (line 11) | function Sidebar({

FILE: frontend/src/components/SkeletonLoader.tsx
  type SkeletonLoaderProps (line 3) | interface SkeletonLoaderProps {

FILE: frontend/src/components/SourcesPopup.tsx
  type SourcesPopupProps (line 17) | type SourcesPopupProps = {
  function SourcesPopup (line 25) | function SourcesPopup({

FILE: frontend/src/components/Spinner.tsx
  type SpinnerProps (line 3) | type SpinnerProps = {
  function Spinner (line 8) | function Spinner({

FILE: frontend/src/components/Table.tsx
  type TableProps (line 3) | interface TableProps {
  type TableContainerProps (line 9) | interface TableContainerProps {
  type TableHeadProps (line 16) | interface TableHeadProps {
  type TableRowProps (line 21) | interface TableRowProps {
  type TableCellProps (line 27) | interface TableCellProps {

FILE: frontend/src/components/TextToSpeechButton.tsx
  constant MAX_CACHE_SIZE (line 20) | const MAX_CACHE_SIZE = 10;
  function getCachedAudio (line 22) | function getCachedAudio(text: string): string | undefined {
  function setCachedAudio (line 31) | function setCachedAudio(text: string, audioBase64: string) {
  function SpeakButton (line 45) | function SpeakButton({ text }: { text: string }) {

FILE: frontend/src/components/ToggleSwitch.tsx
  type ToggleSwitchProps (line 3) | type ToggleSwitchProps = {

FILE: frontend/src/components/ToolsPopup.tsx
  type ToolsPopupProps (line 15) | interface ToolsPopupProps {
  function ToolsPopup (line 21) | function ToolsPopup({

FILE: frontend/src/components/UploadToast.tsx
  constant PROGRESS_RADIUS (line 10) | const PROGRESS_RADIUS = 10;
  constant PROGRESS_CIRCUMFERENCE (line 11) | const PROGRESS_CIRCUMFERENCE = 2 * Math.PI * PROGRESS_RADIUS;
  function UploadToast (line 13) | function UploadToast() {

FILE: frontend/src/components/types/Dropdown.types.ts
  type DropdownOptionBase (line 1) | type DropdownOptionBase = {
  type StringOption (line 6) | type StringOption = string;
  type NameIdOption (line 7) | type NameIdOption = { name: string; id: string } & DropdownOptionBase;
  type LabelValueOption (line 8) | type LabelValueOption = {
  type ValueDescriptionOption (line 12) | type ValueDescriptionOption = {
  type DropdownOption (line 17) | type DropdownOption =
  type DropdownSelectedValue (line 23) | type DropdownSelectedValue = DropdownOption | null;
  type OnSelectHandler (line 25) | type OnSelectHandler<T extends DropdownOption = DropdownOption> = (
  type DropdownProps (line 29) | interface DropdownProps<T extends DropdownOption = DropdownOption> {

FILE: frontend/src/components/types/index.ts
  type InputProps (line 1) | type InputProps = {
  type MermaidRendererProps (line 29) | type MermaidRendererProps = {

FILE: frontend/src/components/ui/button.tsx
  function Button (line 39) | function Button({

FILE: frontend/src/components/ui/command.tsx
  function Command (line 14) | function Command({
  function CommandDialog (line 30) | function CommandDialog({
  function CommandInput (line 61) | function CommandInput({
  function CommandList (line 83) | function CommandList({
  function CommandEmpty (line 99) | function CommandEmpty({
  function CommandGroup (line 111) | function CommandGroup({
  function CommandSeparator (line 127) | function CommandSeparator({
  function CommandItem (line 140) | function CommandItem({
  function CommandShortcut (line 156) | function CommandShortcut({

FILE: frontend/src/components/ui/dialog.tsx
  function Dialog (line 9) | function Dialog({
  function DialogTrigger (line 15) | function DialogTrigger({
  function DialogPortal (line 21) | function DialogPortal({
  function DialogClose (line 27) | function DialogClose({
  function DialogOverlay (line 33) | function DialogOverlay({
  function DialogContent (line 49) | function DialogContent({
  function DialogHeader (line 83) | function DialogHeader({ className, ...props }: React.ComponentProps<'div...
  function DialogFooter (line 93) | function DialogFooter({ className, ...props }: React.ComponentProps<'div...
  function DialogTitle (line 106) | function DialogTitle({
  function DialogDescription (line 119) | function DialogDescription({

FILE: frontend/src/components/ui/input.tsx
  function Input (line 5) | function Input({ className, type, ...props }: React.ComponentProps<'inpu...

FILE: frontend/src/components/ui/label.tsx
  function Label (line 6) | function Label({

FILE: frontend/src/components/ui/multi-select.tsx
  type MultiSelectOption (line 22) | interface MultiSelectOption {
  type MultiSelectProps (line 27) | interface MultiSelectProps {
  function MultiSelect (line 37) | function MultiSelect({

FILE: frontend/src/components/ui/popover.tsx
  function Popover (line 6) | function Popover({
  function PopoverTrigger (line 12) | function PopoverTrigger({
  function PopoverContent (line 18) | function PopoverContent({
  function PopoverAnchor (line 40) | function PopoverAnchor({

FILE: frontend/src/components/ui/select.tsx
  function Select (line 7) | function Select({
  function SelectGroup (line 13) | function SelectGroup({
  function SelectValue (line 19) | function SelectValue({
  function SelectTrigger (line 25) | function SelectTrigger({
  function SelectContent (line 57) | function SelectContent({
  function SelectLabel (line 94) | function SelectLabel({
  function SelectItem (line 107) | function SelectItem({
  function SelectSeparator (line 134) | function SelectSeparator({
  function SelectScrollUpButton (line 147) | function SelectScrollUpButton({
  function SelectScrollDownButton (line 165) | function SelectScrollDownButton({

FILE: frontend/src/components/ui/sheet.tsx
  function Sheet (line 7) | function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive....
  function SheetTrigger (line 11) | function SheetTrigger({
  function SheetClose (line 17) | function SheetClose({
  function SheetPortal (line 23) | function SheetPortal({
  function SheetOverlay (line 29) | function SheetOverlay({
  function SheetContent (line 45) | function SheetContent({
  function SheetHeader (line 86) | function SheetHeader({ className, ...props }: React.ComponentProps<"div"...
  function SheetFooter (line 96) | function SheetFooter({ className, ...props }: React.ComponentProps<"div"...
  function SheetTitle (line 106) | function SheetTitle({
  function SheetDescription (line 119) | function SheetDescription({

FILE: frontend/src/constants/fileUpload.ts
  constant AUDIO_FILE_ACCEPT (line 1) | const AUDIO_FILE_ACCEPT: Record<string, string[]> = {
  constant FILE_UPLOAD_ACCEPT (line 10) | const FILE_UPLOAD_ACCEPT: Record<string, string[]> = {
  constant FILE_UPLOAD_ACCEPT_ATTR (line 35) | const FILE_UPLOAD_ACCEPT_ATTR = [
  constant AUDIO_FILE_ACCEPT_ATTR (line 58) | const AUDIO_FILE_ACCEPT_ATTR = [
  constant SOURCE_FILE_TREE_ACCEPT_ATTR (line 66) | const SOURCE_FILE_TREE_ACCEPT_ATTR = [

FILE: frontend/src/conversation/Conversation.tsx
  function Conversation (line 29) | function Conversation() {

FILE: frontend/src/conversation/ConversationBubble.tsx
  method code (line 477) | code(props) {
  method ul (line 525) | ul({ children }) {
  method ol (line 534) | ol({ children }) {
  method table (line 543) | table({ children }) {
  method thead (line 552) | thead({ children }) {
  method tr (line 559) | tr({ children }) {
  method th (line 566) | th({ children }) {
  method td (line 571) | td({ children }) {
  type AllSourcesProps (line 724) | type AllSourcesProps = {
  function AllSources (line 728) | function AllSources(sources: AllSourcesProps) {
  function ToolCalls (line 789) | function ToolCalls({ toolCalls }: { toolCalls: ToolCallsType[] }) {
  function Thought (line 895) | function Thought({

FILE: frontend/src/conversation/ConversationMessages.tsx
  constant SCROLL_THRESHOLD (line 19) | const SCROLL_THRESHOLD = 10;
  constant LAST_BUBBLE_MARGIN (line 20) | const LAST_BUBBLE_MARGIN = 'mb-32';
  constant DEFAULT_BUBBLE_MARGIN (line 21) | const DEFAULT_BUBBLE_MARGIN = 'mb-7';
  constant FIRST_QUESTION_BUBBLE_MARGIN_TOP (line 22) | const FIRST_QUESTION_BUBBLE_MARGIN_TOP = 'mt-5';
  type ConversationMessagesProps (line 24) | type ConversationMessagesProps = {
  function ConversationMessages (line 44) | function ConversationMessages({

FILE: frontend/src/conversation/ConversationTile.tsx
  type ConversationProps (line 25) | interface ConversationProps {
  type ConversationTileProps (line 29) | interface ConversationTileProps {
  function ConversationTile (line 37) | function ConversationTile({

FILE: frontend/src/conversation/conversationHandlers.ts
  function handleFetchAnswer (line 6) | function handleFetchAnswer(
  function handleFetchAnswerSteaming (line 93) | function handleFetchAnswerSteaming(
  function handleSearch (line 191) | function handleSearch(
  function handleSearchViaApiKey (line 224) | function handleSearchViaApiKey(
  function handleSendFeedback (line 252) | function handleSendFeedback(
  function handleFetchSharedAnswerStreaming (line 280) | function handleFetchSharedAnswerStreaming(
  function handleFetchSharedAnswer (line 354) | function handleFetchSharedAnswer(

FILE: frontend/src/conversation/conversationModels.ts
  type MESSAGE_TYPE (line 3) | type MESSAGE_TYPE = 'QUESTION' | 'ANSWER' | 'ERROR';
  type Status (line 4) | type Status = 'idle' | 'loading' | 'failed';
  type FEEDBACK (line 5) | type FEEDBACK = 'LIKE' | 'DISLIKE' | null;
  type Message (line 7) | interface Message {
  type Attachment (line 12) | interface Attachment {
  type ConversationState (line 21) | interface ConversationState {
  type Answer (line 27) | interface Answer {
  type Query (line 40) | interface Query {
  type RetrievalPayload (line 55) | interface RetrievalPayload {

FILE: frontend/src/conversation/conversationSlice.ts
  constant API_STREAMING (line 23) | const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
  function handleAbort (line 26) | function handleAbort() {
  method addQuery (line 236) | addQuery(state, action: PayloadAction<Query>) {
  method setConversation (line 239) | setConversation(state, action: PayloadAction<Query[]>) {
  method resendQuery (line 242) | resendQuery(
  method updateStreamingQuery (line 260) | updateStreamingQuery(
  method updateConversationId (line 286) | updateConversationId(
  method updateThought (line 293) | updateThought(
  method updateStreamingSource (line 309) | updateStreamingSource(
  method updateToolCall (line 321) | updateToolCall(state, action) {
  method updateQuery (line 340) | updateQuery(
  method setStatus (line 350) | setStatus(state, action: PayloadAction<Status>) {
  method raiseError (line 353) | raiseError(
  method extraReducers (line 374) | extraReducers(builder) {
  type RootState (line 393) | type RootState = ReturnType<typeof store.getState>;

FILE: frontend/src/conversation/sharedConversationSlice.ts
  constant API_STREAMING (line 15) | const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
  type SharedConversationsType (line 16) | interface SharedConversationsType {
  method setStatus (line 149) | setStatus(state, action: PayloadAction<Status>) {
  method setIdentifier (line 152) | setIdentifier(state, action: PayloadAction<string>) {
  method setFetchedData (line 155) | setFetchedData(
  method setClientApiKey (line 174) | setClientApiKey(state, action: PayloadAction<string>) {
  method addQuery (line 177) | addQuery(state, action: PayloadAction<Query>) {
  method updateStreamingQuery (line 180) | updateStreamingQuery(
  method updateToolCalls (line 190) | updateToolCalls(
  method updateQuery (line 199) | updateQuery(
  method updateThought (line 209) | updateThought(
  method updateStreamingSource (line 224) | updateStreamingSource(
  method raiseError (line 238) | raiseError(
  method saveToLocalStorage (line 245) | saveToLocalStorage(state) {
  method extraReducers (line 261) | extraReducers(builder) {
  type RootState (line 303) | type RootState = ReturnType<typeof store.getState>;

FILE: frontend/src/conversation/types/index.ts
  type ToolCallsType (line 1) | type ToolCallsType = {

FILE: frontend/src/hooks/index.ts
  function useOutsideAlerter (line 3) | function useOutsideAlerter<T extends HTMLElement>(
  function useMediaQuery (line 36) | function useMediaQuery() {
  function useDarkTheme (line 68) | function useDarkTheme() {
  function useLoaderState (line 116) | function useLoaderState(

FILE: frontend/src/hooks/useDataInitializer.ts
  function useDataInitializer (line 31) | function useDataInitializer(isAuthLoading: boolean) {

FILE: frontend/src/hooks/useTokenAuth.ts
  function useAuth (line 7) | function useAuth() {

FILE: frontend/src/lib/utils.ts
  function cn (line 4) | function cn(...inputs: ClassValue[]) {

FILE: frontend/src/main.tsx
  function rebuildSbStyles (line 15) | function rebuildSbStyles() {
  function showOverlayScrollbar (line 18) | function showOverlayScrollbar(el: HTMLElement) {

FILE: frontend/src/modals/AddActionModal.tsx
  type AddActionModalProps (line 8) | type AddActionModalProps = {
  function AddActionModal (line 19) | function AddActionModal({

FILE: frontend/src/modals/AddToolModal.tsx
  function AddToolModal (line 15) | function AddToolModal({

FILE: frontend/src/modals/AgentDetailsModal.tsx
  type AgentDetailsModalProps (line 15) | type AgentDetailsModalProps = {
  function AgentDetailsModal (line 22) | function AgentDetailsModal({

FILE: frontend/src/modals/ConfigToolModal.tsx
  type ConfigToolModalProps (line 14) | interface ConfigToolModalProps {
  function ConfigToolModal (line 21) | function ConfigToolModal({

FILE: frontend/src/modals/ConfirmationModal.tsx
  function ConfirmationModal (line 6) | function ConfirmationModal({

FILE: frontend/src/modals/DeleteConvModal.tsx
  function DeleteConvModal (line 9) | function DeleteConvModal({

FILE: frontend/src/modals/FolderManagementModal.tsx
  type FolderNameModalProps (line 7) | type FolderNameModalProps = {
  function FolderNameModal (line 15) | function FolderNameModal({

FILE: frontend/src/modals/ImportSpecModal.tsx
  type ImportSpecModalProps (line 13) | interface ImportSpecModalProps {
  type ParsedResult (line 19) | interface ParsedResult {
  constant METHOD_COLORS (line 29) | const METHOD_COLORS: Record<string, string> = {
  function ImportSpecModal (line 41) | function ImportSpecModal({

FILE: frontend/src/modals/JWTModal.tsx
  type JWTModalProps (line 7) | type JWTModalProps = {
  function JWTModal (line 12) | function JWTModal({

FILE: frontend/src/modals/MCPServerModal.tsx
  type MCPServerModalProps (line 21) | interface MCPServerModalProps {
  function MCPServerModal (line 28) | function MCPServerModal({

FILE: frontend/src/modals/MoveToFolderModal.tsx
  type MoveToFolderModalProps (line 13) | type MoveToFolderModalProps = {
  function MoveToFolderModal (line 22) | function MoveToFolderModal({

FILE: frontend/src/modals/ShareConversationModal.tsx
  type StatusType (line 19) | type StatusType = 'loading' | 'idle' | 'fetched' | 'failed';

FILE: frontend/src/modals/WrapperModal.tsx
  type WrapperModalPropsType (line 6) | type WrapperModalPropsType = {
  function WrapperModal (line 14) | function WrapperModal({

FILE: frontend/src/modals/types/index.ts
  type ConfigFieldSpec (line 1) | type ConfigFieldSpec = {
  type ConfigRequirements (line 13) | type ConfigRequirements = {
  type AvailableToolType (line 17) | type AvailableToolType = {
  type WrapperModalPropsType (line 29) | type WrapperModalPropsType = {

FILE: frontend/src/models/misc.ts
  type ActiveState (line 1) | type ActiveState = 'ACTIVE' | 'INACTIVE';
  type User (line 3) | type User = {
  type Doc (line 6) | type Doc = {
  type GetDocsResponse (line 19) | type GetDocsResponse = {
  type Prompt (line 26) | type Prompt = {
  type PromptProps (line 32) | type PromptProps = {
  type DocumentsProps (line 39) | type DocumentsProps = {

FILE: frontend/src/models/types.ts
  type AvailableModel (line 1) | interface AvailableModel {
  type Model (line 14) | interface Model {

FILE: frontend/src/preferences/PromptsModal.tsx
  type PromptTextareaProps (line 93) | type PromptTextareaProps = {
  function PromptTextarea (line 100) | function PromptTextarea({
  function AddPrompt (line 213) | function AddPrompt({
  function EditPrompt (line 420) | function EditPrompt({
  function PromptsModal (line 641) | function PromptsModal({

FILE: frontend/src/preferences/preferenceApi.ts
  function getDocs (line 7) | async function getDocs(token: string | null): Promise<Doc[] | null> {
  function getDocsWithPagination (line 24) | async function getDocsWithPagination(
  function getConversations (line 53) | async function getConversations(
  function getLocalApiKey (line 88) | function getLocalApiKey(): string | null {
  function parseStoredRecentDocs (line 93) | function parseStoredRecentDocs(docsString: string | null): Doc[] | null {
  function getStoredRecentDocs (line 118) | function getStoredRecentDocs(): Doc[] {
  function getLocalRecentDocs (line 131) | function getLocalRecentDocs(sourceDocs?: Doc[] | null): Doc[] | null {
  function getLocalPrompt (line 153) | function getLocalPrompt(
  function setLocalApiKey (line 181) | function setLocalApiKey(key: string): void {
  function setLocalPrompt (line 185) | function setLocalPrompt(prompt: Prompt): void {
  function setLocalRecentDocs (line 189) | function setLocalRecentDocs(docs: Doc[] | null): void {
  function getPrompts (line 197) | async function getPrompts(token: string | null): Promise<Prompt[]> {

FILE: frontend/src/preferences/preferenceSlice.ts
  type Preference (line 19) | interface Preference {

FILE: frontend/src/preferences/types/index.ts
  type ConversationSummary (line 1) | type ConversationSummary = {
  type GetConversationsResult (line 7) | type GetConversationsResult = {

FILE: frontend/src/settings/Analytics.tsx
  type AnalyticsProps (line 33) | type AnalyticsProps = {
  function Analytics (line 37) | function Analytics({ agentId }: AnalyticsProps) {
  type AnalyticsChartProps (line 338) | type AnalyticsChartProps = {
  function AnalyticsChart (line 345) | function AnalyticsChart({

FILE: frontend/src/settings/General.tsx
  function General (line 18) | function General() {

FILE: frontend/src/settings/Logs.tsx
  type LogsProps (line 13) | type LogsProps = {
  function Logs (line 18) | function Logs({ agentId, tableHeader }: LogsProps) {
  type LogsTableProps (line 72) | type LogsTableProps = {
  function LogsTable (line 78) | function LogsTable({ logs, setPage, loading, tableHeader }: LogsTablePro...
  function Log (line 149) | function Log({

FILE: frontend/src/settings/Prompts.tsx
  type ExtendedPromptProps (line 13) | type ExtendedPromptProps = PromptProps & {
  function Prompts (line 20) | function Prompts({

FILE: frontend/src/settings/Sources.tsx
  function Sources (line 50) | function Sources({

FILE: frontend/src/settings/ToolConfig.tsx
  constant METHOD_COLORS (line 27) | const METHOD_COLORS: Record<string, string> = {
  function ToolConfig (line 39) | function ToolConfig({
  function APIToolConfig (line 748) | function APIToolConfig({
  function APIActionTable (line 1149) | function APIActionTable({

FILE: frontend/src/settings/Tools.tsx
  function Tools (line 24) | function Tools() {

FILE: frontend/src/settings/index.tsx
  function Settings (line 29) | function Settings() {

FILE: frontend/src/settings/types/index.ts
  type ChunkType (line 3) | type ChunkType = {
  type APIKeyData (line 9) | type APIKeyData = {
  type LogData (line 18) | type LogData = {
  type ParameterGroupType (line 30) | type ParameterGroupType = {
  type UserToolType (line 43) | type UserToolType = {
  type APIActionType (line 75) | type APIActionType = {
  type APIToolType (line 99) | type APIToolType = {

FILE: frontend/src/store.ts
  type RootState (line 75) | type RootState = ReturnType<typeof store.getState>;
  type AppDispatch (line 76) | type AppDispatch = typeof store.dispatch;

FILE: frontend/src/upload/Upload.tsx
  function Upload (line 39) | function Upload({

FILE: frontend/src/upload/types/ingestor.ts
  type IngestorType (line 10) | type IngestorType =
  type IngestorConfig (line 20) | interface IngestorConfig {
  type IngestorFormData (line 26) | type IngestorFormData = {
  type FieldType (line 33) | type FieldType =
  type FormField (line 43) | interface FormField {
  type IngestorSchema (line 52) | interface IngestorSchema {
  type IngestorOption (line 266) | interface IngestorOption {

FILE: frontend/src/upload/uploadSlice.ts
  type Attachment (line 4) | interface Attachment {
  type UploadTaskStatus (line 13) | type UploadTaskStatus =
  type UploadTask (line 20) | interface UploadTask {
  type UploadState (line 30) | interface UploadState {

FILE: frontend/src/utils/browserUtils.ts
  function getOS (line 1) | function getOS() {
  function isTouchDevice (line 8) | function isTouchDevice() {

FILE: frontend/src/utils/chartUtils.ts
  method afterUpdate (line 25) | afterUpdate(chart: ChartJS, args: any, options: { containerID: string }) {

FILE: frontend/src/utils/dateTimeUtils.ts
  function formatDate (line 1) | function formatDate(dateString: string): string {

FILE: frontend/src/utils/objectUtils.ts
  function areObjectsEqual (line 7) | function areObjectsEqual(obj1: any, obj2: any): boolean {

FILE: frontend/src/utils/stringUtils.ts
  function truncate (line 1) | function truncate(str: string, n: number) {
  function formatBytes (line 6) | function formatBytes(bytes: number | null): string {

FILE: md-gen.py
  function create_markdown_from_directory (line 3) | def create_markdown_from_directory(directory=".", output_file="combined....

FILE: scripts/migrate_conversation_id_dbref_to_objectid.py
  function backup_collection (line 20) | def backup_collection(collection, backup_collection_name):
  function migrate_conversation_id_dbref_to_objectid (line 26) | def migrate_conversation_id_dbref_to_objectid():

FILE: scripts/migrate_to_v1_vectorstore.py
  function backup_collection (line 16) | def backup_collection(collection, backup_collection_name):
  function migrate_to_v1_vectorstore_mongo (line 21) | def migrate_to_v1_vectorstore_mongo():
  function migrate_faiss_to_v1_vectorstore (line 48) | def migrate_faiss_to_v1_vectorstore():
  function migrate_mongo_atlas_vector_to_v1_vectorstore (line 66) | def migrate_mongo_atlas_vector_to_v1_vectorstore():

FILE: tests/agents/test_agent_creator.py
  class TestAgentCreator (line 8) | class TestAgentCreator:
    method test_create_classic_agent (line 10) | def test_create_classic_agent(self, agent_base_params):
    method test_create_react_agent (line 17) | def test_create_react_agent(self, agent_base_params):
    method test_create_agent_case_insensitive (line 23) | def test_create_agent_case_insensitive(self, agent_base_params):
    method test_create_agent_invalid_type (line 30) | def test_create_agent_invalid_type(self, agent_base_params):
    method test_agent_registry_contains_expected_agents (line 34) | def test_agent_registry_contains_expected_agents(self):
    method test_create_agent_with_optional_params (line 40) | def test_create_agent_with_optional_params(self, agent_base_params):
    method test_create_agent_with_attachments (line 51) | def test_create_agent_with_attachments(self, agent_base_params):

FILE: tests/agents/test_base_agent.py
  class TestBaseAgentInitialization (line 9) | class TestBaseAgentInitialization:
    method test_agent_initialization (line 11) | def test_agent_initialization(
    method test_agent_initialization_with_none_chat_history (line 25) | def test_agent_initialization_with_none_chat_history(
    method test_agent_initialization_with_chat_history (line 32) | def test_agent_initialization_with_chat_history(
    method test_agent_decoded_token_defaults_to_empty_dict (line 44) | def test_agent_decoded_token_defaults_to_empty_dict(
    method test_agent_user_extracted_from_token (line 52) | def test_agent_user_extracted_from_token(
  class TestBaseAgentBuildMessages (line 61) | class TestBaseAgentBuildMessages:
    method test_build_messages_basic (line 63) | def test_build_messages_basic(
    method test_build_messages_with_chat_history (line 78) | def test_build_messages_with_chat_history(
    method test_build_messages_with_tool_calls_in_history (line 99) | def test_build_messages_with_tool_calls_in_history(
    method test_build_messages_handles_missing_filename (line 122) | def test_build_messages_handles_missing_filename(
    method test_build_messages_uses_title_as_fallback (line 132) | def test_build_messages_uses_title_as_fallback(
    method test_build_messages_uses_source_as_fallback (line 139) | def test_build_messages_uses_source_as_fallback(
  class TestBaseAgentTools (line 148) | class TestBaseAgentTools:
    method test_get_user_tools (line 150) | def test_get_user_tools(
    method test_get_user_tools_filters_by_status (line 172) | def test_get_user_tools_filters_by_status(
    method test_get_tools_by_api_key (line 192) | def test_get_tools_by_api_key(
    method test_build_tool_parameters (line 220) | def test_build_tool_parameters(
    method test_prepare_tools_with_api_tool (line 250) | def test_prepare_tools_with_api_tool(
    method test_prepare_tools_with_regular_tool (line 279) | def test_prepare_tools_with_regular_tool(
    method test_prepare_tools_filters_inactive_actions (line 303) | def test_prepare_tools_filters_inactive_actions(
  class TestBaseAgentToolExecution (line 335) | class TestBaseAgentToolExecution:
    method test_execute_tool_action_success (line 337) | def test_execute_tool_action_success(
    method test_execute_tool_action_invalid_tool_name (line 372) | def test_execute_tool_action_invalid_tool_name(
    method test_execute_tool_action_tool_not_found (line 393) | def test_execute_tool_action_tool_not_found(
    method test_execute_tool_action_with_parameters (line 411) | def test_execute_tool_action_with_parameters(
    method test_get_truncated_tool_calls (line 449) | def test_get_truncated_tool_calls(
  class TestBaseAgentLLMGeneration (line 472) | class TestBaseAgentLLMGeneration:
    method test_llm_gen_basic (line 474) | def test_llm_gen_basic(
    method test_llm_gen_with_tools (line 492) | def test_llm_gen_with_tools(
    method test_llm_gen_with_json_schema (line 510) | def test_llm_gen_with_json_schema(
  class TestBaseAgentHandleResponse (line 535) | class TestBaseAgentHandleResponse:
    method test_handle_response_string (line 537) | def test_handle_response_string(
    method test_handle_response_with_message (line 548) | def test_handle_response_with_message(
    method test_handle_response_with_structured_output (line 562) | def test_handle_response_with_structured_output(
    method test_handle_response_with_handler (line 581) | def test_handle_response_with_handler(

FILE: tests/agents/test_classic_agent.py
  class TestClassicAgent (line 8) | class TestClassicAgent:
    method test_classic_agent_initialization (line 10) | def test_classic_agent_initialization(
    method test_gen_inner_basic_flow (line 19) | def test_gen_inner_basic_flow(
    method test_gen_inner_retrieves_documents (line 51) | def test_gen_inner_retrieves_documents(
    method test_gen_inner_uses_user_api_key_tools (line 71) | def test_gen_inner_uses_user_api_key_tools(
    method test_gen_inner_uses_user_tools (line 106) | def test_gen_inner_uses_user_tools(
    method test_gen_inner_builds_correct_messages (line 134) | def test_gen_inner_builds_correct_messages(
    method test_gen_inner_logs_tool_calls (line 162) | def test_gen_inner_logs_tool_calls(
  class TestClassicAgentIntegration (line 190) | class TestClassicAgentIntegration:
    method test_gen_method_with_logging (line 192) | def test_gen_method_with_logging(
    method test_gen_method_decorator_applied (line 214) | def test_gen_method_decorator_applied(

FILE: tests/agents/test_get_artifact.py
  class TestGetArtifact (line 9) | class TestGetArtifact:
    method test_note_artifact_success (line 10) | def test_note_artifact_success(self, mock_mongo_db, flask_app, decoded...
    method test_todo_artifact_success (line 37) | def test_todo_artifact_success(self, mock_mongo_db, flask_app, decoded...
    method test_todo_artifact_all_param (line 85) | def test_todo_artifact_all_param(self, mock_mongo_db, flask_app, decod...
    method test_invalid_artifact_id_returns_400 (line 146) | def test_invalid_artifact_id_returns_400(self, mock_mongo_db, flask_ap...
    method test_artifact_not_found_returns_404 (line 158) | def test_artifact_not_found_returns_404(self, mock_mongo_db, flask_app...
    method test_other_user_artifact_returns_404 (line 172) | def test_other_user_artifact_returns_404(self, mock_mongo_db, flask_ap...

FILE: tests/agents/test_react_agent.py
  class TestReActAgent (line 8) | class TestReActAgent:
    method test_react_agent_initialization (line 10) | def test_react_agent_initialization(
    method test_react_agent_inherits_base_properties (line 19) | def test_react_agent_inherits_base_properties(
  class TestReActAgentContentExtraction (line 30) | class TestReActAgentContentExtraction:
    method test_extract_content_from_string (line 32) | def test_extract_content_from_string(
    method test_extract_content_from_message_object (line 42) | def test_extract_content_from_message_object(
    method test_extract_content_from_openai_response (line 55) | def test_extract_content_from_openai_response(
    method test_extract_content_from_anthropic_response (line 71) | def test_extract_content_from_anthropic_response(
    method test_extract_content_from_openai_stream (line 88) | def test_extract_content_from_openai_stream(
    method test_extract_content_from_anthropic_stream (line 108) | def test_extract_content_from_anthropic_stream(
    method test_extract_content_from_string_stream (line 130) | def test_extract_content_from_string_stream(
    method test_extract_content_handles_none_content (line 140) | def test_extract_content_handles_none_content(
  class TestReActAgentPlanning (line 157) | class TestReActAgentPlanning:
    method test_planning_phase (line 164) | def test_planning_phase(
    method test_planning_phase_fills_template (line 194) | def test_planning_phase_fills_template(
  class TestReActAgentFinalAnswer (line 215) | class TestReActAgentFinalAnswer:
    method test_synthesis_phase (line 222) | def test_synthesis_phase(
    method test_synthesis_phase_truncates_long_observations (line 247) | def test_synthesis_phase_truncates_long_observations(
    method test_synthesis_phase_no_tools (line 269) | def test_synthesis_phase_no_tools(
  class TestReActAgentGenInner (line 290) | class TestReActAgentGenInner:
    method test_gen_inner_resets_state (line 295) | def test_gen_inner_resets_state(
    method test_gen_inner_stops_on_satisfied (line 323) | def test_gen_inner_stops_on_satisfied(
    method test_gen_inner_max_iterations (line 359) | def test_gen_inner_max_iterations(
    method test_gen_inner_yields_sources (line 394) | def test_gen_inner_yields_sources(
    method test_gen_inner_yields_tool_calls (line 419) | def test_gen_inner_yields_tool_calls(
    method test_gen_inner_logs_observations (line 447) | def test_gen_inner_logs_observations(
  class TestReActAgentIntegration (line 472) | class TestReActAgentIntegration:
    method test_full_react_workflow (line 479) | def test_full_react_workflow(

FILE: tests/agents/test_tool_action_parser.py
  class TestToolActionParser (line 8) | class TestToolActionParser:
    method test_parser_initialization (line 10) | def test_parser_initialization(self):
    method test_parse_openai_llm_valid_call (line 16) | def test_parse_openai_llm_valid_call(self):
    method test_parse_openai_llm_with_underscore_in_action (line 29) | def test_parse_openai_llm_with_underscore_in_action(self):
    method test_parse_openai_llm_invalid_format_no_underscore (line 42) | def test_parse_openai_llm_invalid_format_no_underscore(self):
    method test_parse_openai_llm_non_numeric_tool_id (line 55) | def test_parse_openai_llm_non_numeric_tool_id(self):
    method test_parse_openai_llm_malformed_json (line 67) | def test_parse_openai_llm_malformed_json(self):
    method test_parse_openai_llm_missing_attributes (line 80) | def test_parse_openai_llm_missing_attributes(self):
    method test_parse_google_llm_valid_call (line 91) | def test_parse_google_llm_valid_call(self):
    method test_parse_google_llm_with_complex_action_name (line 104) | def test_parse_google_llm_with_complex_action_name(self):
    method test_parse_google_llm_invalid_format (line 116) | def test_parse_google_llm_invalid_format(self):
    method test_parse_google_llm_missing_attributes (line 129) | def test_parse_google_llm_missing_attributes(self):
    method test_parse_unknown_llm_type_defaults_to_openai (line 140) | def test_parse_unknown_llm_type_defaults_to_openai(self):
    method test_parse_args_empty_arguments_openai (line 153) | def test_parse_args_empty_arguments_openai(self):
    method test_parse_args_empty_arguments_google (line 166) | def test_parse_args_empty_arguments_google(self):
    method test_parse_args_with_special_characters (line 179) | def test_parse_args_with_special_characters(self):
    method test_parse_args_with_nested_objects (line 192) | def test_parse_args_with_nested_objects(self):

FILE: tests/agents/test_tool_manager.py
  class MockTool (line 8) | class MockTool(Tool):
    method __init__ (line 9) | def __init__(self, config):
    method execute_action (line 12) | def execute_action(self, action_name: str, **kwargs):
    method get_actions_metadata (line 15) | def get_actions_metadata(self):
    method get_config_requirements (line 18) | def get_config_requirements(self):
  class TestToolManager (line 23) | class TestToolManager:
    method test_tool_manager_initialization (line 26) | def test_tool_manager_initialization(self, mock_iter):
    method test_load_tools_skips_base_and_private (line 37) | def test_load_tools_skips_base_and_private(self, mock_import, mock_iter):
    method test_load_tools_creates_tool_instances (line 55) | def test_load_tools_creates_tool_instances(self, mock_iter):
    method test_load_tool_with_user_id (line 67) | def test_load_tool_with_user_id(self):
    method test_load_tool_without_user_id (line 79) | def test_load_tool_without_user_id(self):
    method test_load_tool_updates_config (line 89) | def test_load_tool_updates_config(self, mock_iter):
    method test_execute_action_on_loaded_tool (line 102) | def test_execute_action_on_loaded_tool(self, mock_import, mock_iter):
    method test_execute_action_tool_not_loaded (line 119) | def test_execute_action_tool_not_loaded(self):
    method test_execute_action_with_user_id_for_mcp_tool (line 129) | def test_execute_action_with_user_id_for_mcp_tool(self, mock_import):
    method test_execute_action_with_user_id_for_memory_tool (line 145) | def test_execute_action_with_user_id_for_memory_tool(self, mock_import):
    method test_get_all_actions_metadata (line 162) | def test_get_all_actions_metadata(self, mock_import, mock_iter):
    method test_get_all_actions_metadata_empty (line 181) | def test_get_all_actions_metadata_empty(self, mock_iter):
    method test_load_tool_with_notes_tool (line 191) | def test_load_tool_with_notes_tool(self):
  class TestToolBase (line 202) | class TestToolBase:
    method test_tool_base_is_abstract (line 204) | def test_tool_base_is_abstract(self):
    method test_mock_tool_implements_interface (line 208) | def test_mock_tool_implements_interface(self):
    method test_mock_tool_execute_action (line 215) | def test_mock_tool_execute_action(self):
    method test_mock_tool_get_actions_metadata (line 222) | def test_mock_tool_get_actions_metadata(self):
    method test_mock_tool_get_config_requirements (line 230) | def test_mock_tool_get_config_requirements(self):

FILE: tests/agents/test_workflow_engine.py
  class StubNodeAgent (line 18) | class StubNodeAgent:
    method __init__ (line 19) | def __init__(self, events):
    method gen (line 22) | def gen(self, _prompt):
  function create_engine (line 26) | def create_engine() -> WorkflowEngine:
  function create_agent_node (line 39) | def create_agent_node(
  function test_execute_agent_node_saves_structured_output_as_json (line 66) | def test_execute_agent_node_saves_structured_output_as_json(monkeypatch):
  function test_execute_agent_node_normalizes_wrapped_schema_before_agent_create (line 95) | def test_execute_agent_node_normalizes_wrapped_schema_before_agent_creat...
  function test_execute_agent_node_falls_back_to_text_when_schema_not_configured (line 128) | def test_execute_agent_node_falls_back_to_text_when_schema_not_configure...
  function test_validate_workflow_structure_rejects_invalid_agent_json_schema (line 149) | def test_validate_workflow_structure_rejects_invalid_agent_json_schema():
  function test_validate_workflow_structure_accepts_valid_agent_json_schema (line 173) | def test_validate_workflow_structure_accepts_valid_agent_json_schema():
  function test_validate_workflow_structure_accepts_wrapped_agent_json_schema (line 194) | def test_validate_workflow_structure_accepts_wrapped_agent_json_schema():
  function test_validate_workflow_structure_accepts_output_variable_and_schema_together (line 215) | def test_validate_workflow_structure_accepts_output_variable_and_schema_...
  function test_validate_workflow_structure_rejects_unsupported_structured_output_model (line 239) | def test_validate_workflow_structure_rejects_unsupported_structured_outp...
  function test_execute_agent_node_raises_when_structured_output_violates_schema (line 275) | def test_execute_agent_node_raises_when_structured_output_violates_schem...
  function test_execute_agent_node_raises_when_schema_set_and_response_not_json (line 306) | def test_execute_agent_node_raises_when_schema_set_and_response_not_json...

FILE: tests/agents/test_workflow_template.py
  function create_engine (line 7) | def create_engine() -> WorkflowEngine:
  function test_workflow_template_supports_agent_namespace_and_legacy_variables (line 20) | def test_workflow_template_supports_agent_namespace_and_legacy_variables():
  function test_workflow_template_supports_global_namespaces (line 31) | def test_workflow_template_supports_global_namespaces():
  function test_workflow_template_handles_namespace_conflicts_with_agent_prefix (line 45) | def test_workflow_template_handles_namespace_conflicts_with_agent_prefix():
  function test_workflow_template_gracefully_handles_invalid_template_syntax (line 56) | def test_workflow_template_gracefully_handles_invalid_template_syntax():

FILE: tests/api/answer/routes/test_base.py
  class TestBaseAnswerValidation (line 9) | class TestBaseAnswerValidation:
    method test_validate_request_passes_with_required_fields (line 10) | def test_validate_request_passes_with_required_fields(
    method test_validate_request_fails_without_question (line 23) | def test_validate_request_fails_without_question(self, mock_mongo_db, ...
    method test_validate_with_conversation_id_required (line 36) | def test_validate_with_conversation_id_required(self, mock_mongo_db, f...
    method test_validate_passes_with_all_required_fields (line 49) | def test_validate_passes_with_all_required_fields(self, mock_mongo_db,...
  class TestUsageChecking (line 62) | class TestUsageChecking:
    method test_returns_none_when_no_api_key (line 63) | def test_returns_none_when_no_api_key(self, mock_mongo_db, flask_app):
    method test_returns_error_for_invalid_api_key (line 74) | def test_returns_error_for_invalid_api_key(self, mock_mongo_db, flask_...
    method test_checks_token_limit_when_enabled (line 88) | def test_checks_token_limit_when_enabled(self, mock_mongo_db, flask_app):
    method test_checks_request_limit_when_enabled (line 113) | def test_checks_request_limit_when_enabled(self, mock_mongo_db, flask_...
    method test_uses_default_limits_when_not_specified (line 138) | def test_uses_default_limits_when_not_specified(self, mock_mongo_db, f...
    method test_exceeds_token_limit (line 162) | def test_exceeds_token_limit(self, mock_mongo_db, flask_app):
    method test_exceeds_request_limit (line 203) | def test_exceeds_request_limit(self, mock_mongo_db, flask_app):
    method test_both_limits_disabled_returns_none (line 244) | def test_both_limits_disabled_returns_none(self, mock_mongo_db, flask_...
  class TestGPTModelRetrieval (line 270) | class TestGPTModelRetrieval:
    method test_initializes_gpt_model (line 271) | def test_initializes_gpt_model(self, mock_mongo_db, flask_app):
  class TestConversationServiceIntegration (line 282) | class TestConversationServiceIntegration:
    method test_initializes_conversation_service (line 283) | def test_initializes_conversation_service(self, mock_mongo_db, flask_a...
    method test_has_access_to_user_logs_collection (line 292) | def test_has_access_to_user_logs_collection(self, mock_mongo_db, flask...
  class TestCompleteStreamMethod (line 303) | class TestCompleteStreamMethod:
    method test_streams_answer_chunks (line 304) | def test_streams_answer_chunks(self, mock_mongo_db, flask_app):
    method test_streams_sources (line 336) | def test_streams_sources(self, mock_mongo_db, flask_app):
    method test_handles_error_during_streaming (line 367) | def test_handles_error_during_streaming(self, mock_mongo_db, flask_app):
    method test_saves_conversation_when_enabled (line 391) | def test_saves_conversation_when_enabled(self, mock_mongo_db, flask_app):
    method test_logs_to_user_logs_collection (line 424) | def test_logs_to_user_logs_collection(self, mock_mongo_db, flask_app):
  class TestProcessResponseStream (line 464) | class TestProcessResponseStream:
    method test_processes_complete_stream (line 465) | def test_processes_complete_stream(self, mock_mongo_db, flask_app):
    method test_handles_stream_error (line 489) | def test_handles_stream_error(self, mock_mongo_db, flask_app):
    method test_handles_malformed_stream_data (line 508) | def test_handles_malformed_stream_data(self, mock_mongo_db, flask_app):
  class TestErrorStreamGenerate (line 525) | class TestErrorStreamGenerate:
    method test_generates_error_stream (line 526) | def test_generates_error_stream(self, mock_mongo_db, flask_app):

FILE: tests/api/answer/routes/test_search.py
  class TestSearchResourceValidation (line 9) | class TestSearchResourceValidation:
    method test_returns_error_when_question_missing (line 10) | def test_returns_error_when_question_missing(self, mock_mongo_db, flas...
    method test_returns_error_when_api_key_missing (line 23) | def test_returns_error_when_api_key_missing(self, mock_mongo_db, flask...
    method test_returns_error_for_invalid_api_key (line 36) | def test_returns_error_for_invalid_api_key(self, mock_mongo_db, flask_...
  class TestGetSourcesFromApiKey (line 51) | class TestGetSourcesFromApiKey:
    method test_returns_empty_list_when_agent_not_found (line 52) | def test_returns_empty_list_when_agent_not_found(self, mock_mongo_db, ...
    method test_returns_source_id_from_dbref (line 62) | def test_returns_source_id_from_dbref(self, mock_mongo_db, flask_app):
    method test_returns_multiple_sources_from_sources_array (line 91) | def test_returns_multiple_sources_from_sources_array(
    method test_skips_default_source_in_sources_array (line 125) | def test_skips_default_source_in_sources_array(self, mock_mongo_db, fl...
    method test_skips_default_source_in_legacy_field (line 152) | def test_skips_default_source_in_legacy_field(self, mock_mongo_db, fla...
    method test_falls_back_to_legacy_source_when_sources_empty (line 174) | def test_falls_back_to_legacy_source_when_sources_empty(
    method test_handles_string_source_id (line 203) | def test_handles_string_source_id(self, mock_mongo_db, flask_app):
  class TestSearchVectorstores (line 229) | class TestSearchVectorstores:
    method test_returns_empty_when_no_source_ids (line 230) | def test_returns_empty_when_no_source_ids(self, mock_mongo_db, flask_a...
    method test_skips_empty_source_ids (line 240) | def test_skips_empty_source_ids(self, mock_mongo_db, flask_app):
    method test_returns_search_results (line 258) | def test_returns_search_results(self, mock_mongo_db, flask_app):
    method test_handles_langchain_document_format (line 287) | def test_handles_langchain_document_format(self, mock_mongo_db, flask_...
    method test_respects_chunks_limit (line 310) | def test_respects_chunks_limit(self, mock_mongo_db, flask_app):
    method test_deduplicates_results (line 332) | def test_deduplicates_results(self, mock_mongo_db, flask_app):
    method test_handles_vectorstore_error_gracefully (line 356) | def test_handles_vectorstore_error_gracefully(self, mock_mongo_db, fla...
    method test_uses_filename_as_title_fallback (line 371) | def test_uses_filename_as_title_fallback(self, mock_mongo_db, flask_app):
    method test_uses_content_snippet_as_title_last_resort (line 393) | def test_uses_content_snippet_as_title_last_resort(self, mock_mongo_db...
  class TestSearchEndpoint (line 418) | class TestSearchEndpoint:
    method test_returns_empty_array_when_no_sources (line 419) | def test_returns_empty_array_when_no_sources(self, mock_mongo_db, flas...
    method test_returns_search_results_successfully (line 445) | def test_returns_search_results_successfully(self, mock_mongo_db, flas...
    method test_uses_default_chunks_value (line 489) | def test_uses_default_chunks_value(self, mock_mongo_db, flask_app):
    method test_handles_internal_error (line 527) | def test_handles_internal_error(self, mock_mongo_db, flask_app):

FILE: tests/api/answer/services/test_conversation_service.py
  class TestConversationServiceGet (line 8) | class TestConversationServiceGet:
    method test_returns_none_when_no_conversation_id (line 10) | def test_returns_none_when_no_conversation_id(self, mock_mongo_db):
    method test_returns_none_when_no_user_id (line 20) | def test_returns_none_when_no_user_id(self, mock_mongo_db):
    method test_returns_conversation_for_owner (line 30) | def test_returns_conversation_for_owner(self, mock_mongo_db):
    method test_returns_none_for_unauthorized_user (line 54) | def test_returns_none_for_unauthorized_user(self, mock_mongo_db):
    method test_converts_objectid_to_string (line 72) | def test_converts_objectid_to_string(self, mock_mongo_db):
  class TestConversationServiceSave (line 91) | class TestConversationServiceSave:
    method test_raises_error_when_no_user_in_token (line 93) | def test_raises_error_when_no_user_in_token(self, mock_mongo_db):
    method test_truncates_long_source_text (line 115) | def test_truncates_long_source_text(self, mock_mongo_db):
    method test_creates_new_conversation_with_summary (line 149) | def test_creates_new_conversation_with_summary(self, mock_mongo_db):
    method test_appends_to_existing_conversation (line 181) | def test_appends_to_existing_conversation(self, mock_mongo_db):
    method test_prevents_unauthorized_conversation_update (line 217) | def test_prevents_unauthorized_conversation_update(self, mock_mongo_db):

FILE: tests/api/answer/services/test_prompt_renderer.py
  class TestTemplateEngine (line 5) | class TestTemplateEngine:
    method test_render_simple_template (line 7) | def test_render_simple_template(self):
    method test_render_with_namespace (line 15) | def test_render_with_namespace(self):
    method test_render_empty_template (line 29) | def test_render_empty_template(self):
    method test_render_template_without_variables (line 37) | def test_render_template_without_variables(self):
    method test_render_undefined_variable_returns_empty_string (line 45) | def test_render_undefined_variable_returns_empty_string(self):
    method test_render_syntax_error_raises_error (line 53) | def test_render_syntax_error_raises_error(self):
    method test_validate_template_valid (line 64) | def test_validate_template_valid(self):
    method test_validate_template_invalid (line 70) | def test_validate_template_invalid(self):
    method test_validate_empty_template (line 76) | def test_validate_empty_template(self):
    method test_extract_variables (line 82) | def test_extract_variables(self):
  class TestSystemNamespace (line 94) | class TestSystemNamespace:
    method test_system_namespace_build (line 96) | def test_system_namespace_build(self):
    method test_system_namespace_generates_request_id (line 110) | def test_system_namespace_generates_request_id(self):
    method test_system_namespace_name (line 119) | def test_system_namespace_name(self):
    method test_system_namespace_date_format (line 125) | def test_system_namespace_date_format(self):
  class TestPassthroughNamespace (line 138) | class TestPassthroughNamespace:
    method test_passthrough_namespace_build (line 140) | def test_passthrough_namespace_build(self):
    method test_passthrough_namespace_empty (line 152) | def test_passthrough_namespace_empty(self):
    method test_passthrough_namespace_filters_unsafe_values (line 160) | def test_passthrough_namespace_filters_unsafe_values(self):
    method test_passthrough_namespace_allows_none_values (line 180) | def test_passthrough_namespace_allows_none_values(self):
    method test_passthrough_namespace_name (line 190) | def test_passthrough_namespace_name(self):
  class TestSourceNamespace (line 198) | class TestSourceNamespace:
    method test_source_namespace_build_with_docs (line 200) | def test_source_namespace_build_with_docs(self):
    method test_source_namespace_build_empty (line 217) | def test_source_namespace_build_empty(self):
    method test_source_namespace_build_docs_only (line 225) | def test_source_namespace_build_docs_only(self):
    method test_source_namespace_build_docs_together_only (line 237) | def test_source_namespace_build_docs_together_only(self):
    method test_source_namespace_name (line 249) | def test_source_namespace_name(self):
  class TestToolsNamespace (line 257) | class TestToolsNamespace:
    method test_tools_namespace_build_with_memory_data (line 259) | def test_tools_namespace_build_with_memory_data(self):
    method test_tools_namespace_build_empty (line 272) | def test_tools_namespace_build_empty(self):
    method test_tools_namespace_build_multiple_tools (line 280) | def test_tools_namespace_build_multiple_tools(self):
    method test_tools_namespace_filters_unsafe_values (line 299) | def test_tools_namespace_filters_unsafe_values(self):
    method test_tools_namespace_name (line 314) | def test_tools_namespace_name(self):
    method test_tools_namespace_with_empty_dict (line 320) | def test_tools_namespace_with_empty_dict(self):
  class TestNamespaceManagerWithTools (line 330) | class TestNamespaceManagerWithTools:
    method test_namespace_manager_includes_tools_in_context (line 332) | def test_namespace_manager_includes_tools_in_context(self):
    method test_namespace_manager_build_context_all_namespaces (line 343) | def test_namespace_manager_build_context_all_namespaces(self):
    method test_namespace_manager_build_context_partial_data (line 361) | def test_namespace_manager_build_context_partial_data(self):
    method test_namespace_manager_get_builder (line 370) | def test_namespace_manager_get_builder(self):
    method test_namespace_manager_get_builder_nonexistent (line 378) | def test_namespace_manager_get_builder_nonexistent(self):
    method test_namespace_manager_handles_builder_exceptions (line 386) | def test_namespace_manager_handles_builder_exceptions(self):
  class TestPromptRenderer (line 406) | class TestPromptRenderer:
    method test_render_prompt_with_template_syntax (line 408) | def test_render_prompt_with_template_syntax(self):
    method test_render_prompt_with_passthrough_data (line 419) | def test_render_prompt_with_passthrough_data(self):
    method test_render_prompt_with_source_docs (line 431) | def test_render_prompt_with_source_docs(self):
    method test_render_prompt_empty_content (line 443) | def test_render_prompt_empty_content(self):
    method test_render_prompt_legacy_format_with_summaries (line 451) | def test_render_prompt_legacy_format_with_summaries(self):
    method test_render_prompt_legacy_format_without_docs (line 462) | def test_render_prompt_legacy_format_without_docs(self):
    method test_render_prompt_combined_namespace_variables (line 472) | def test_render_prompt_combined_namespace_variables(self):
    method test_render_prompt_with_tools_data (line 490) | def test_render_prompt_with_tools_data(self):
    method test_render_prompt_with_all_namespaces (line 507) | def test_render_prompt_with_all_namespaces(self):
    method test_render_prompt_undefined_variable_returns_empty_string (line 533) | def test_render_prompt_undefined_variable_returns_empty_string(self):
    method test_render_prompt_with_undefined_variable_in_template (line 542) | def test_render_prompt_with_undefined_variable_in_template(self):
    method test_validate_template_valid (line 551) | def test_validate_template_valid(self):
    method test_validate_template_invalid (line 557) | def test_validate_template_invalid(self):
    method test_extract_variables (line 563) | def test_extract_variables(self):
    method test_uses_template_syntax_detection (line 573) | def test_uses_template_syntax_detection(self):
    method test_apply_legacy_substitutions (line 582) | def test_apply_legacy_substitutions(self):
    method test_apply_legacy_substitutions_without_docs (line 593) | def test_apply_legacy_substitutions_without_docs(self):
  class TestPromptRendererIntegration (line 605) | class TestPromptRendererIntegration:
    method test_render_prompt_real_world_scenario (line 607) | def test_render_prompt_real_world_scenario(self):
    method test_render_prompt_multiple_doc_references (line 630) | def test_render_prompt_multiple_doc_references(self):
  class TestStreamProcessorPromptRendering (line 643) | class TestStreamProcessorPromptRendering:
    method test_stream_processor_pre_fetch_docs_none_doc_mode (line 645) | def test_stream_processor_pre_fetch_docs_none_doc_mode(self, mock_mong...
    method test_pre_fetch_tools_disabled_globally (line 656) | def test_pre_fetch_tools_disabled_globally(self, mock_mongo_db, monkey...
    method test_pre_fetch_tools_disabled_per_request (line 669) | def test_pre_fetch_tools_disabled_per_request(self, mock_mongo_db):
    method test_pre_fetch_tools_skips_tool_with_no_actions (line 679) | def test_pre_fetch_tools_skips_tool_with_no_actions(self, mock_mongo_db):
    method test_pre_fetch_tools_enabled_by_default (line 717) | def test_pre_fetch_tools_enabled_by_default(self, mock_mongo_db, monke...
    method test_pre_fetch_tools_no_tools_configured (line 759) | def test_pre_fetch_tools_no_tools_configured(self, mock_mongo_db):
    method test_pre_fetch_tools_memory_returns_error (line 769) | def test_pre_fetch_tools_memory_returns_error(self, mock_mongo_db):
    method test_pre_fetch_tools_memory_returns_empty (line 810) | def test_pre_fetch_tools_memory_returns_empty(self, mock_mongo_db):

FILE: tests/api/answer/services/test_stream_processor.py
  class TestGetPromptFunction (line 6) | class TestGetPromptFunction:
    method test_loads_custom_prompt_from_database (line 8) | def test_loads_custom_prompt_from_database(self, mock_mongo_db):
    method test_raises_error_for_invalid_prompt_id (line 26) | def test_raises_error_for_invalid_prompt_id(self, mock_mongo_db):
    method test_raises_error_for_malformed_id (line 35) | def test_raises_error_for_malformed_id(self, mock_mongo_db):
  class TestStreamProcessorInitialization (line 46) | class TestStreamProcessorInitialization:
    method test_initializes_with_decoded_token (line 48) | def test_initializes_with_decoded_token(self, mock_mongo_db):
    method test_initializes_without_token (line 64) | def test_initializes_without_token(self, mock_mongo_db):
    method test_initializes_default_attributes (line 75) | def test_initializes_default_attributes(self, mock_mongo_db):
    method test_extracts_conversation_id_from_request (line 89) | def test_extracts_conversation_id_from_request(self, mock_mongo_db):
  class TestStreamProcessorHistoryLoading (line 101) | class TestStreamProcessorHistoryLoading:
    method test_loads_history_from_existing_conversation (line 103) | def test_loads_history_from_existing_conversation(self, mock_mongo_db):
    method test_raises_error_for_unauthorized_conversation (line 136) | def test_raises_error_for_unauthorized_conversation(self, mock_mongo_db):
    method test_uses_request_history_when_no_conversation_id (line 161) | def test_uses_request_history_when_no_conversation_id(self, mock_mongo...
  class TestStreamProcessorAgentConfiguration (line 175) | class TestStreamProcessorAgentConfiguration:
    method test_configures_agent_from_valid_api_key (line 177) | def test_configures_agent_from_valid_api_key(self, mock_mongo_db):
    method test_uses_default_config_without_api_key (line 206) | def test_uses_default_config_without_api_key(self, mock_mongo_db):
    method test_conversation_agent_overrides_request_active_docs (line 217) | def test_conversation_agent_overrides_request_active_docs(self, mock_m...
  class TestStreamProcessorDocPrefetch (line 279) | class TestStreamProcessorDocPrefetch:
    method test_prefetch_not_skipped_for_agent_when_isNoneDoc_true (line 281) | def test_prefetch_not_skipped_for_agent_when_isNoneDoc_true(self, mock...
  class TestStreamProcessorAttachments (line 335) | class TestStreamProcessorAttachments:
    method test_processes_attachments_from_request (line 337) | def test_processes_attachments_from_request(self, mock_mongo_db):
    method test_handles_empty_attachments (line 359) | def test_handles_empty_attachments(self, mock_mongo_db):
  class TestToolPreFetch (line 374) | class TestToolPreFetch:
    method test_cryptoprice_prefetch_with_saved_parameters (line 377) | def test_cryptoprice_prefetch_with_saved_parameters(self, mock_mongo_db):
    method test_prefetch_with_missing_saved_values_uses_defaults (line 486) | def test_prefetch_with_missing_saved_values_uses_defaults(self, mock_m...
    method test_prefetch_with_tool_id_reference (line 572) | def test_prefetch_with_tool_id_reference(self, mock_mongo_db):
    method test_prefetch_with_multiple_same_name_tools (line 635) | def test_prefetch_with_multiple_same_name_tools(self, mock_mongo_db):

FILE: tests/api/conftest.py
  function auth_headers (line 8) | def auth_headers():
  function mock_request_token (line 13) | def mock_request_token(monkeypatch, decoded_token):
  function sample_conversation (line 28) | def sample_conversation():
  function sample_prompt (line 44) | def sample_prompt():
  function sample_agent (line 55) | def sample_agent():
  function sample_answer_request (line 69) | def sample_answer_request():
  function flask_app (line 84) | def flask_app():

FILE: tests/api/user/attachments/test_routes.py
  function _get_response_status (line 8) | def _get_response_status(response):
  function _get_response_json (line 14) | def _get_response_json(response):
  class FakeRedis (line 20) | class FakeRedis:
    method __init__ (line 21) | def __init__(self):
    method get (line 24) | def get(self, key):
    method setex (line 27) | def setex(self, key, ttl, value):
    method delete (line 31) | def delete(self, key):
  class TestStoreAttachmentEndpoint (line 35) | class TestStoreAttachmentEndpoint:
    method test_store_attachment_preserves_upload_indexes_for_partial_failures (line 37) | def test_store_attachment_preserves_upload_indexes_for_partial_failures(
    method test_store_attachment_rejects_oversized_audio_files (line 83) | def test_store_attachment_rejects_oversized_audio_files(
  class TestSpeechToTextEndpoint (line 112) | class TestSpeechToTextEndpoint:
    method test_stt_returns_400_when_file_is_missing (line 113) | def test_stt_returns_400_when_file_is_missing(self, flask_app, mock_mo...
    method test_stt_returns_401_when_authentication_is_missing (line 127) | def test_stt_returns_401_when_authentication_is_missing(
    method test_stt_transcribes_audio_for_authenticated_user (line 149) | def test_stt_transcribes_audio_for_authenticated_user(
    method test_stt_rejects_unsupported_extension (line 191) | def test_stt_rejects_unsupported_extension(self, flask_app, mock_mongo...
  class TestLiveSpeechToTextEndpoint (line 211) | class TestLiveSpeechToTextEndpoint:
    method test_live_stt_start_creates_session (line 213) | def test_live_stt_start_creates_session(
    method test_live_stt_chunk_reconciles_transcript_progressively (line 240) | def test_live_stt_chunk_reconciles_transcript_progressively(
    method test_live_stt_chunk_rejects_missing_session (line 359) | def test_live_stt_chunk_rejects_missing_session(
    method test_live_stt_chunk_hides_internal_value_errors (line 387) | def test_live_stt_chunk_hides_internal_value_errors(

FILE: tests/api/user/sources/test_audio_upload.py
  function test_upload_route_passes_audio_extensions_to_ingest (line 9) | def test_upload_route_passes_audio_extensions_to_ingest(flask_app, mock_...
  function test_upload_route_rejects_oversized_audio (line 46) | def test_upload_route_rejects_oversized_audio(

FILE: tests/api/user/sources/test_routes.py
  class TestGetProviderFromRemoteData (line 9) | class TestGetProviderFromRemoteData:
    method test_returns_none_for_none_input (line 12) | def test_returns_none_for_none_input(self):
    method test_returns_none_for_empty_string (line 19) | def test_returns_none_for_empty_string(self):
    method test_extracts_provider_from_dict (line 26) | def test_extracts_provider_from_dict(self):
    method test_extracts_provider_from_json_string (line 34) | def test_extracts_provider_from_json_string(self):
    method test_returns_none_for_dict_without_provider (line 42) | def test_returns_none_for_dict_without_provider(self):
    method test_returns_none_for_invalid_json (line 50) | def test_returns_none_for_invalid_json(self):
    method test_returns_none_for_json_array (line 57) | def test_returns_none_
Condensed preview — 642 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,563K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 374,
    "preview": "FROM python:3.12-bookworm\n\n# Install Node.js 20.x\nRUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \\\n    &&"
  },
  {
    "path": ".devcontainer/devc-welcome.md",
    "chars": 1358,
    "preview": "# Welcome to DocsGPT Devcontainer\n\nWelcome to the DocsGPT development environment! This guide will help you get started "
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 560,
    "preview": "{\n\t\"name\": \"DocsGPT Dev Container\",\n\t\"dockerComposeFile\": [\"docker-compose-dev.yaml\", \"docker-compose.override.yaml\"],\n\t"
  },
  {
    "path": ".devcontainer/docker-compose-dev.yaml",
    "chars": 225,
    "preview": "services:\n\n  redis:\n    image: redis:6-alpine\n    ports:\n      - 6379:6379\n\n  mongo:\n    image: mongo:6\n    ports:\n     "
  },
  {
    "path": ".devcontainer/docker-compose.override.yaml",
    "chars": 835,
    "preview": "version: '3.8'\n\nservices:\n  dev:\n    build:\n      context: .\n      dockerfile: Dockerfile\n    volumes:\n      - ../:/work"
  },
  {
    "path": ".devcontainer/post-create-command.sh",
    "chars": 1303,
    "preview": "#!/bin/bash\n\nset -e  # Exit immediately if a command exits with a non-zero status\n\nif [ ! -f frontend/.env.development ]"
  },
  {
    "path": ".env-template",
    "chars": 1146,
    "preview": "API_KEY=<LLM api key (for example, open ai key)>\nLLM_NAME=docsgpt\nVITE_API_STREAMING=true\nINTERNAL_KEY=<internal key for"
  },
  {
    "path": ".gitattributes",
    "chars": 66,
    "preview": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 61,
    "preview": "# These are supported funding model platforms\n\ngithub: arc53\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "chars": 3991,
    "preview": "name: \"🐛 Bug Report\"\ndescription: \"Submit a bug report to help us improve\"\ntitle: \"🐛 Bug Report: \"\nlabels: [\"type: bug\"]"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "chars": 1904,
    "preview": "name: 🚀 Feature\ndescription: \"Submit a proposal for a new feature\"\ntitle: \"🚀 Feature: \"\nlabels: [feature]\nbody:\n  - type"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "chars": 189,
    "preview": "- **What kind of change does this PR introduce?** (Bug fix, feature, docs update, ...)\n\n- **Why was this change needed?*"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 906,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/holopin.yml",
    "chars": 289,
    "preview": "organization: docsgpt\ndefaultSticker: cm1ulwkkl180570cl82rtzympu\nstickers:\n  - id: cm1ulwkkl180570cl82rtzympu\n    alias:"
  },
  {
    "path": ".github/labeler.yml",
    "chars": 534,
    "preview": "repo:\n- changed-files:\n  - any-glob-to-any-file: '*'\n\ngithub:\n- changed-files:\n  - any-glob-to-any-file: '.github/**/*'\n"
  },
  {
    "path": ".github/styles/DocsGPT/Spelling.yml",
    "chars": 212,
    "preview": "extends: spelling\nlevel: warning\nmessage: \"Did you really mean '%s'?\"\nignore:\n  - \"**/node_modules/**\"\n  - \"**/dist/**\"\n"
  },
  {
    "path": ".github/styles/config/vocabularies/DocsGPT/accept.txt",
    "chars": 331,
    "preview": "Ollama\nQdrant\nMilvus\nChatwoot\nNextra\nVSCode\nnpm\nLLMs\nAPIs\nGroq\nSGLang\nLMDeploy\nOAuth\nVite\nLLM\nJSONPath\nUIs\nconfigs\nuncom"
  },
  {
    "path": ".github/workflows/bandit.yaml",
    "chars": 1000,
    "preview": "name: Bandit Security Scan\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    types: [opened, synchronize, reop"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 4413,
    "preview": "name: Build and push DocsGPT Docker image\n\non:\n  release:\n    types: [published]\n\njobs:\n  build:\n    if: github.reposito"
  },
  {
    "path": ".github/workflows/cife.yml",
    "chars": 4466,
    "preview": "name: Build and push DocsGPT-FE Docker image\n\non:\n  release:\n    types: [published]\n\njobs:\n  build:\n    if: github.repos"
  },
  {
    "path": ".github/workflows/docker-develop-build.yml",
    "chars": 3211,
    "preview": "name: Build and push multi-arch DocsGPT Docker image\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - main\n\njobs:"
  },
  {
    "path": ".github/workflows/docker-develop-fe-build.yml",
    "chars": 3397,
    "preview": "name: Build and push DocsGPT FE Docker image for development\n\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - mai"
  },
  {
    "path": ".github/workflows/labeler.yml",
    "chars": 383,
    "preview": "# https://github.com/actions/labeler\nname: Pull Request Labeler\non:\n  - pull_request_target\njobs:\n  triage:\n    if: gith"
  },
  {
    "path": ".github/workflows/lint.yml",
    "chars": 299,
    "preview": "name: Python linting\n\non:\n  push:\n    branches:\n      - '*'\n  pull_request:\n    types: [ opened, synchronize ]\n\npermissi"
  },
  {
    "path": ".github/workflows/pytest.yml",
    "chars": 1160,
    "preview": "name: Run python tests with pytest\non: [push, pull_request]\n\npermissions:\n  contents: read\n\njobs:\n  pytest_and_coverage:"
  },
  {
    "path": ".github/workflows/sync_fork.yaml",
    "chars": 1255,
    "preview": "name: Upstream Sync\n\npermissions:\n  contents: write\n\non:\n  schedule:\n    - cron: \"0 0 * * *\" # every hour\n  workflow_dis"
  },
  {
    "path": ".github/workflows/vale.yml",
    "chars": 569,
    "preview": "name: Vale Documentation Linter\n\non:\n  pull_request:\n    paths:\n      - 'docs/**/*.md'\n      - 'docs/**/*.mdx'\n      - '"
  },
  {
    "path": ".gitignore",
    "chars": 2596,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\nexperiments/\n\nexperiments\n# C extensions\n*.so\n"
  },
  {
    "path": ".ruff.toml",
    "chars": 207,
    "preview": "# Allow lines to be as long as 120 characters.\nline-length = 120\n\n[lint.per-file-ignores]\n# Integration tests use sys.pa"
  },
  {
    "path": ".vale.ini",
    "chars": 90,
    "preview": "MinAlertLevel = warning\nStylesPath = .github/styles\n\n[*.{md,mdx}]\nBasedOnStyles = DocsGPT\n"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 1626,
    "preview": "{\n  \"version\": \"0.2.0\",\n  \"configurations\": [\n    {\n      \"name\": \"Frontend Debug (npm)\",\n      \"type\": \"node-terminal\","
  },
  {
    "path": "AGENTS.md",
    "chars": 4333,
    "preview": "# AGENTS.md\n\n- Read `CONTRIBUTING.md` before making non-trivial changes.\n- For day-to-day development and feature work, "
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 5301,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors and leaders pledge to make participat"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 6729,
    "preview": "# Welcome to DocsGPT Contributing Guidelines\n\nThank you for choosing to contribute to DocsGPT! We are all very grateful!"
  },
  {
    "path": "HACKTOBERFEST.md",
    "chars": 2248,
    "preview": "# **🎉 Join the Hacktoberfest with DocsGPT and win a Free T-shirt for a meaningful PR! 🎉**\n\nWelcome, contributors! We're "
  },
  {
    "path": "LICENSE",
    "chars": 1062,
    "preview": "MIT License\n\nCopyright (c) 2023 arc53\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
  },
  {
    "path": "README.md",
    "chars": 7792,
    "preview": "<h1 align=\"center\">\n  DocsGPT  🦖\n</h1>\n\n<p align=\"center\">\n  <strong>Private AI for agents, assistants and enterprise se"
  },
  {
    "path": "SECURITY.md",
    "chars": 261,
    "preview": "# Security Policy\n\n## Supported Versions\n\nSupported Versions:\n\nCurrently, we support security patches by committing chan"
  },
  {
    "path": "application/Dockerfile",
    "chars": 2643,
    "preview": "# Builder Stage\nFROM ubuntu:24.04 as builder\n\nENV DEBIAN_FRONTEND=noninteractive\n\nRUN apt-get update && \\\n    apt-get in"
  },
  {
    "path": "application/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/agents/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/agents/agent_creator.py",
    "chars": 627,
    "preview": "import logging\n\nfrom application.agents.classic_agent import ClassicAgent\nfrom application.agents.react_agent import ReA"
  },
  {
    "path": "application/agents/base.py",
    "chars": 27397,
    "preview": "import logging\nimport uuid\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Generator, List, Optional\n\nfrom "
  },
  {
    "path": "application/agents/classic_agent.py",
    "chars": 1126,
    "preview": "import logging\nfrom typing import Dict, Generator\n\nfrom application.agents.base import BaseAgent\nfrom application.loggin"
  },
  {
    "path": "application/agents/react_agent.py",
    "chars": 9195,
    "preview": "import logging\nimport os\nfrom typing import Any, Dict, Generator, List\n\nfrom application.agents.base import BaseAgent\nfr"
  },
  {
    "path": "application/agents/tools/api_body_serializer.py",
    "chars": 12069,
    "preview": "import base64\nimport json\nimport logging\nfrom enum import Enum\nfrom typing import Any, Dict, Optional, Union\nfrom urllib"
  },
  {
    "path": "application/agents/tools/api_tool.py",
    "chars": 10185,
    "preview": "import json\nimport logging\nimport re\nfrom typing import Any, Dict, Optional\nfrom urllib.parse import urlencode\n\nimport r"
  },
  {
    "path": "application/agents/tools/base.py",
    "chars": 507,
    "preview": "from abc import ABC, abstractmethod\n\n\nclass Tool(ABC):\n    @abstractmethod\n    def execute_action(self, action_name: str"
  },
  {
    "path": "application/agents/tools/brave.py",
    "chars": 6011,
    "preview": "import logging\n\nimport requests\n\nfrom application.agents.tools.base import Tool\n\nlogger = logging.getLogger(__name__)\n\n\n"
  },
  {
    "path": "application/agents/tools/cryptoprice.py",
    "chars": 2756,
    "preview": "import requests\nfrom application.agents.tools.base import Tool\n\n\nclass CryptoPriceTool(Tool):\n    \"\"\"\n    CryptoPrice\n  "
  },
  {
    "path": "application/agents/tools/duckduckgo.py",
    "chars": 7254,
    "preview": "import logging\nimport time\nfrom typing import Any, Dict, Optional\n\nfrom application.agents.tools.base import Tool\n\nlogge"
  },
  {
    "path": "application/agents/tools/mcp_tool.py",
    "chars": 38379,
    "preview": "import asyncio\nimport base64\nimport concurrent.futures\nimport json\nimport logging\nimport time\nfrom typing import Any, Di"
  },
  {
    "path": "application/agents/tools/memory.py",
    "chars": 19868,
    "preview": "from datetime import datetime\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional\nimport re\nimport uui"
  },
  {
    "path": "application/agents/tools/notes.py",
    "chars": 8453,
    "preview": "from datetime import datetime\nfrom typing import Any, Dict, List, Optional\nimport uuid\n\nfrom .base import Tool\nfrom appl"
  },
  {
    "path": "application/agents/tools/ntfy.py",
    "chars": 4565,
    "preview": "import requests\nfrom application.agents.tools.base import Tool\n\nclass NtfyTool(Tool):\n    \"\"\"\n    Ntfy Tool\n    A tool f"
  },
  {
    "path": "application/agents/tools/postgres.py",
    "chars": 6272,
    "preview": "import logging\n\nimport psycopg2\n\nfrom application.agents.tools.base import Tool\n\nlogger = logging.getLogger(__name__)\n\n\n"
  },
  {
    "path": "application/agents/tools/read_webpage.py",
    "chars": 3116,
    "preview": "import requests\nfrom markdownify import markdownify\nfrom application.agents.tools.base import Tool\nfrom application.core"
  },
  {
    "path": "application/agents/tools/spec_parser.py",
    "chars": 11413,
    "preview": "\"\"\"\nAPI Specification Parser\n\nParses OpenAPI 3.x and Swagger 2.0 specifications and converts them\nto API Tool action def"
  },
  {
    "path": "application/agents/tools/telegram.py",
    "chars": 3458,
    "preview": "import logging\n\nimport requests\n\nfrom application.agents.tools.base import Tool\n\nlogger = logging.getLogger(__name__)\n\n\n"
  },
  {
    "path": "application/agents/tools/todo_list.py",
    "chars": 11769,
    "preview": "from datetime import datetime\nfrom typing import Any, Dict, List, Optional\nimport uuid\n\nfrom .base import Tool\nfrom appl"
  },
  {
    "path": "application/agents/tools/tool_action_parser.py",
    "chars": 2453,
    "preview": "import json\nimport logging\n\nlogger = logging.getLogger(__name__)\n\n\nclass ToolActionParser:\n    def __init__(self, llm_ty"
  },
  {
    "path": "application/agents/tools/tool_manager.py",
    "chars": 2060,
    "preview": "import importlib\nimport inspect\nimport os\nimport pkgutil\n\nfrom application.agents.tools.base import Tool\n\n\nclass ToolMan"
  },
  {
    "path": "application/agents/workflow_agent.py",
    "chars": 8876,
    "preview": "import logging\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, Generator, Optional\n\nfrom applicati"
  },
  {
    "path": "application/agents/workflows/cel_evaluator.py",
    "chars": 2200,
    "preview": "from typing import Any, Dict\n\nimport celpy\nimport celpy.celtypes\n\n\nclass CelEvaluationError(Exception):\n    pass\n\n\ndef _"
  },
  {
    "path": "application/agents/workflows/node_agent.py",
    "chars": 3063,
    "preview": "\"\"\"Workflow Node Agents - defines specialized agents for workflow nodes.\"\"\"\n\nfrom typing import Any, Dict, List, Optiona"
  },
  {
    "path": "application/agents/workflows/schemas.py",
    "chars": 7067,
    "preview": "from datetime import datetime, timezone\nfrom enum import Enum\nfrom typing import Any, Dict, List, Literal, Optional, Uni"
  },
  {
    "path": "application/agents/workflows/workflow_engine.py",
    "chars": 17491,
    "preview": "import json\nimport logging\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, Generator, List, Option"
  },
  {
    "path": "application/api/__init__.py",
    "chars": 121,
    "preview": "from flask_restx import Api\n\napi = Api(\n    version=\"1.0\",\n    title=\"DocsGPT API\",\n    description=\"API for DocsGPT\",\n)"
  },
  {
    "path": "application/api/answer/__init__.py",
    "chars": 584,
    "preview": "from flask import Blueprint\n\nfrom application.api import api\nfrom application.api.answer.routes.answer import AnswerReso"
  },
  {
    "path": "application/api/answer/routes/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/api/answer/routes/answer.py",
    "chars": 5383,
    "preview": "import logging\nimport traceback\n\nfrom flask import make_response, request\nfrom flask_restx import fields, Resource\n\nfrom"
  },
  {
    "path": "application/api/answer/routes/base.py",
    "chars": 19219,
    "preview": "import datetime\nimport json\nimport logging\nfrom typing import Any, Dict, Generator, List, Optional\n\nfrom flask import js"
  },
  {
    "path": "application/api/answer/routes/search.py",
    "chars": 6459,
    "preview": "import logging\nfrom typing import Any, Dict, List\n\nfrom flask import make_response, request\nfrom flask_restx import fiel"
  },
  {
    "path": "application/api/answer/routes/stream.py",
    "chars": 5479,
    "preview": "import logging\nimport traceback\n\nfrom flask import request, Response\nfrom flask_restx import fields, Resource\n\nfrom appl"
  },
  {
    "path": "application/api/answer/services/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/api/answer/services/compression/__init__.py",
    "chars": 499,
    "preview": "\"\"\"\nCompression module for managing conversation context compression.\n\n\"\"\"\n\nfrom application.api.answer.services.compres"
  },
  {
    "path": "application/api/answer/services/compression/message_builder.py",
    "chars": 9473,
    "preview": "\"\"\"Message reconstruction utilities for compression.\"\"\"\n\nimport logging\nimport uuid\nfrom typing import Dict, List, Optio"
  },
  {
    "path": "application/api/answer/services/compression/orchestrator.py",
    "chars": 8020,
    "preview": "\"\"\"High-level compression orchestration.\"\"\"\n\nimport logging\nfrom typing import Any, Dict, Optional\n\nfrom application.api"
  },
  {
    "path": "application/api/answer/services/compression/prompt_builder.py",
    "chars": 5307,
    "preview": "\"\"\"Compression prompt building logic.\"\"\"\n\nimport logging\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Op"
  },
  {
    "path": "application/api/answer/services/compression/service.py",
    "chars": 10901,
    "preview": "\"\"\"Core compression service with simplified responsibilities.\"\"\"\n\nimport logging\nimport re\nfrom datetime import datetime"
  },
  {
    "path": "application/api/answer/services/compression/threshold_checker.py",
    "chars": 3586,
    "preview": "\"\"\"Compression threshold checking logic.\"\"\"\n\nimport logging\nfrom typing import Any, Dict\n\nfrom application.core.model_ut"
  },
  {
    "path": "application/api/answer/services/compression/token_counter.py",
    "chars": 3462,
    "preview": "\"\"\"Token counting utilities for compression.\"\"\"\n\nimport logging\nfrom typing import Any, Dict, List\n\nfrom application.uti"
  },
  {
    "path": "application/api/answer/services/compression/types.py",
    "chars": 2616,
    "preview": "\"\"\"Type definitions for compression module.\"\"\"\n\nfrom dataclasses import dataclass, field\nfrom datetime import datetime\nf"
  },
  {
    "path": "application/api/answer/services/conversation_service.py",
    "chars": 11032,
    "preview": "import logging\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, List, Optional\n\nfrom application.co"
  },
  {
    "path": "application/api/answer/services/prompt_renderer.py",
    "chars": 3456,
    "preview": "import logging\nfrom typing import Any, Dict, Optional\n\nfrom application.templates.namespaces import NamespaceManager\n\nfr"
  },
  {
    "path": "application/api/answer/services/stream_processor.py",
    "chars": 32737,
    "preview": "import datetime\nimport json\nimport logging\nimport os\nfrom pathlib import Path\nfrom typing import Any, Dict, Optional, Se"
  },
  {
    "path": "application/api/connector/routes.py",
    "chars": 22940,
    "preview": "import base64\nimport datetime\nimport html\nimport json\nimport uuid\nfrom urllib.parse import urlencode\n\n\nfrom bson.objecti"
  },
  {
    "path": "application/api/internal/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/api/internal/routes.py",
    "chars": 5373,
    "preview": "import os\nimport datetime\nimport json\nfrom flask import Blueprint, request, send_from_directory, jsonify\nfrom werkzeug.u"
  },
  {
    "path": "application/api/user/__init__.py",
    "chars": 110,
    "preview": "\"\"\"User API module - provides all user-related API endpoints\"\"\"\n\nfrom .routes import user\n\n__all__ = [\"user\"]\n"
  },
  {
    "path": "application/api/user/agents/__init__.py",
    "chars": 260,
    "preview": "\"\"\"Agents module.\"\"\"\n\nfrom .routes import agents_ns\nfrom .sharing import agents_sharing_ns\nfrom .webhooks import agents_"
  },
  {
    "path": "application/api/user/agents/folders.py",
    "chars": 11045,
    "preview": "\"\"\"\nAgent folders management routes.\nProvides virtual folder organization for agents (Google Drive-like structure).\n\"\"\"\n"
  },
  {
    "path": "application/api/user/agents/routes.py",
    "chars": 57633,
    "preview": "\"\"\"Agent management routes.\"\"\"\n\nimport datetime\nimport json\nimport uuid\n\nfrom bson.dbref import DBRef\nfrom bson.objectid"
  },
  {
    "path": "application/api/user/agents/sharing.py",
    "chars": 11074,
    "preview": "\"\"\"Agent management sharing functionality.\"\"\"\n\nimport datetime\nimport secrets\n\nfrom bson import DBRef\nfrom bson.objectid"
  },
  {
    "path": "application/api/user/agents/webhooks.py",
    "chars": 4576,
    "preview": "\"\"\"Agent management webhook handlers.\"\"\"\n\nimport secrets\n\nfrom bson.objectid import ObjectId\nfrom flask import current_a"
  },
  {
    "path": "application/api/user/analytics/__init__.py",
    "chars": 86,
    "preview": "\"\"\"Analytics module.\"\"\"\n\nfrom .routes import analytics_ns\n\n__all__ = [\"analytics_ns\"]\n"
  },
  {
    "path": "application/api/user/analytics/routes.py",
    "chars": 19804,
    "preview": "\"\"\"Analytics and reporting routes.\"\"\"\n\nimport datetime\n\nfrom bson.objectid import ObjectId\nfrom flask import current_app"
  },
  {
    "path": "application/api/user/attachments/__init__.py",
    "chars": 92,
    "preview": "\"\"\"Attachments module.\"\"\"\n\nfrom .routes import attachments_ns\n\n__all__ = [\"attachments_ns\"]\n"
  },
  {
    "path": "application/api/user/attachments/routes.py",
    "chars": 23777,
    "preview": "\"\"\"File attachments and media routes.\"\"\"\n\nimport os\nimport tempfile\nfrom pathlib import Path\n\nfrom bson.objectid import "
  },
  {
    "path": "application/api/user/base.py",
    "chars": 7334,
    "preview": "\"\"\"\nShared utilities, database connections, and helper functions for user API routes.\n\"\"\"\n\nimport datetime\nimport os\nimp"
  },
  {
    "path": "application/api/user/conversations/__init__.py",
    "chars": 108,
    "preview": "\"\"\"Conversation management module.\"\"\"\n\nfrom .routes import conversations_ns\n\n__all__ = [\"conversations_ns\"]\n"
  },
  {
    "path": "application/api/user/conversations/routes.py",
    "chars": 11173,
    "preview": "\"\"\"Conversation management routes.\"\"\"\n\nimport datetime\n\nfrom bson.objectid import ObjectId\nfrom flask import current_app"
  },
  {
    "path": "application/api/user/models/__init__.py",
    "chars": 55,
    "preview": "from .routes import models_ns\n\n__all__ = [\"models_ns\"]\n"
  },
  {
    "path": "application/api/user/models/routes.py",
    "chars": 948,
    "preview": "from flask import current_app, jsonify, make_response\nfrom flask_restx import Namespace, Resource\n\nfrom application.core"
  },
  {
    "path": "application/api/user/prompts/__init__.py",
    "chars": 80,
    "preview": "\"\"\"Prompts module.\"\"\"\n\nfrom .routes import prompts_ns\n\n__all__ = [\"prompts_ns\"]\n"
  },
  {
    "path": "application/api/user/prompts/routes.py",
    "chars": 7453,
    "preview": "\"\"\"Prompt management routes.\"\"\"\n\nimport os\n\nfrom bson.objectid import ObjectId\nfrom flask import current_app, jsonify, m"
  },
  {
    "path": "application/api/user/routes.py",
    "chars": 1331,
    "preview": "\"\"\"\nMain user API routes - registers all namespace modules.\n\"\"\"\n\nfrom flask import Blueprint\n\nfrom application.api impor"
  },
  {
    "path": "application/api/user/sharing/__init__.py",
    "chars": 80,
    "preview": "\"\"\"Sharing module.\"\"\"\n\nfrom .routes import sharing_ns\n\n__all__ = [\"sharing_ns\"]\n"
  },
  {
    "path": "application/api/user/sharing/routes.py",
    "chars": 12902,
    "preview": "\"\"\"Conversation sharing routes.\"\"\"\n\nimport uuid\n\nfrom bson.binary import Binary, UuidRepresentation\nfrom bson.dbref impo"
  },
  {
    "path": "application/api/user/sources/__init__.py",
    "chars": 198,
    "preview": "\"\"\"Sources module.\"\"\"\n\nfrom .chunks import sources_chunks_ns\nfrom .routes import sources_ns\nfrom .upload import sources_"
  },
  {
    "path": "application/api/user/sources/chunks.py",
    "chars": 11324,
    "preview": "\"\"\"Source document management chunk management.\"\"\"\n\nfrom bson.objectid import ObjectId\nfrom flask import current_app, js"
  },
  {
    "path": "application/api/user/sources/routes.py",
    "chars": 15781,
    "preview": "\"\"\"Source document management routes.\"\"\"\n\nimport json\nimport math\n\nfrom bson.objectid import ObjectId\nfrom flask import "
  },
  {
    "path": "application/api/user/sources/upload.py",
    "chars": 27034,
    "preview": "\"\"\"Source document management upload functionality.\"\"\"\n\nimport json\nimport os\nimport tempfile\nimport zipfile\n\nfrom bson."
  },
  {
    "path": "application/api/user/tasks.py",
    "chars": 3062,
    "preview": "from datetime import timedelta\n\nfrom application.celery_init import celery\nfrom application.worker import (\n    agent_we"
  },
  {
    "path": "application/api/user/tools/__init__.py",
    "chars": 120,
    "preview": "\"\"\"Tools module.\"\"\"\n\nfrom .mcp import tools_mcp_ns\nfrom .routes import tools_ns\n\n__all__ = [\"tools_ns\", \"tools_mcp_ns\"]\n"
  },
  {
    "path": "application/api/user/tools/mcp.py",
    "chars": 18403,
    "preview": "\"\"\"Tool management MCP server integration.\"\"\"\n\nimport json\nfrom urllib.parse import urlencode, urlparse\n\nfrom bson.objec"
  },
  {
    "path": "application/api/user/tools/routes.py",
    "chars": 27853,
    "preview": "\"\"\"Tool management routes.\"\"\"\n\nfrom bson.objectid import ObjectId\nfrom flask import current_app, jsonify, make_response,"
  },
  {
    "path": "application/api/user/utils.py",
    "chars": 10376,
    "preview": "\"\"\"Centralized utilities for API routes.\"\"\"\n\nfrom functools import wraps\nfrom typing import Any, Callable, Dict, List, O"
  },
  {
    "path": "application/api/user/workflows/__init__.py",
    "chars": 61,
    "preview": "from .routes import workflows_ns\n\n__all__ = [\"workflows_ns\"]\n"
  },
  {
    "path": "application/api/user/workflows/routes.py",
    "chars": 19833,
    "preview": "\"\"\"Workflow management routes.\"\"\"\n\nfrom datetime import datetime, timezone\nfrom typing import Any, Dict, List, Optional,"
  },
  {
    "path": "application/app.py",
    "chars": 4069,
    "preview": "import os\nimport platform\nimport uuid\n\nimport dotenv\nfrom flask import Flask, jsonify, redirect, request\nfrom jose impor"
  },
  {
    "path": "application/auth.py",
    "chars": 792,
    "preview": "from jose import jwt\n\nfrom application.core.settings import settings\n\n\ndef handle_auth(request, data={}):\n    if setting"
  },
  {
    "path": "application/cache.py",
    "chars": 4392,
    "preview": "import json\nimport logging\nimport time\nfrom threading import Lock\n\nimport redis\n\nfrom application.core.settings import s"
  },
  {
    "path": "application/celery_init.py",
    "chars": 563,
    "preview": "from celery import Celery\nfrom application.core.settings import settings\nfrom celery.signals import setup_logging\n\n\ndef "
  },
  {
    "path": "application/celeryconfig.py",
    "chars": 250,
    "preview": "import os\n\nbroker_url = os.getenv(\"CELERY_BROKER_URL\")\nresult_backend = os.getenv(\"CELERY_RESULT_BACKEND\")\n\ntask_seriali"
  },
  {
    "path": "application/core/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/core/json_schema_utils.py",
    "chars": 1042,
    "preview": "from typing import Any, Dict, Optional\n\n\nclass JsonSchemaValidationError(ValueError):\n    \"\"\"Raised when a JSON schema p"
  },
  {
    "path": "application/core/logging_config.py",
    "chars": 566,
    "preview": "from logging.config import dictConfig\n\ndef setup_logging():\n    dictConfig({\n        'version': 1,\n        'formatters':"
  },
  {
    "path": "application/core/model_configs.py",
    "chars": 6868,
    "preview": "\"\"\"\nModel configurations for all supported LLM providers.\n\"\"\"\n\nfrom application.core.model_settings import (\n    Availab"
  },
  {
    "path": "application/core/model_settings.py",
    "chars": 10885,
    "preview": "import logging\nfrom dataclasses import dataclass, field\nfrom enum import Enum\nfrom typing import Dict, List, Optional\n\nl"
  },
  {
    "path": "application/core/model_utils.py",
    "chars": 3001,
    "preview": "from typing import Any, Dict, Optional\n\nfrom application.core.model_settings import ModelRegistry\n\n\ndef get_api_key_for_"
  },
  {
    "path": "application/core/mongo_db.py",
    "chars": 579,
    "preview": "from application.core.settings import settings\nfrom pymongo import MongoClient\n\n\nclass MongoDB:\n    _client = None\n\n    "
  },
  {
    "path": "application/core/settings.py",
    "chars": 8409,
    "preview": "import os\nfrom pathlib import Path\nfrom typing import Optional\n\nfrom pydantic import field_validator\nfrom pydantic_setti"
  },
  {
    "path": "application/core/url_validation.py",
    "chars": 5047,
    "preview": "\"\"\"\nURL validation utilities to prevent SSRF (Server-Side Request Forgery) attacks.\n\nThis module provides functions to v"
  },
  {
    "path": "application/error.py",
    "chars": 1667,
    "preview": "from flask import jsonify\nfrom werkzeug.http import HTTP_STATUS_CODES\n\n\ndef response_error(code_status, message=None):\n "
  },
  {
    "path": "application/llm/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/llm/anthropic.py",
    "chars": 6770,
    "preview": "import base64\nimport logging\n\nfrom anthropic import AI_PROMPT, Anthropic, HUMAN_PROMPT\n\nfrom application.core.settings i"
  },
  {
    "path": "application/llm/base.py",
    "chars": 5194,
    "preview": "import logging\nfrom abc import ABC, abstractmethod\n\nfrom application.cache import gen_cache, stream_cache\n\nfrom applicat"
  },
  {
    "path": "application/llm/docsgpt_provider.py",
    "chars": 1503,
    "preview": "from application.core.settings import settings\nfrom application.llm.openai import OpenAILLM\n\nDOCSGPT_API_KEY = \"sk-docsg"
  },
  {
    "path": "application/llm/google_ai.py",
    "chars": 23843,
    "preview": "import logging\n\nfrom google import genai\nfrom google.genai import types\n\nfrom application.core.settings import settings\n"
  },
  {
    "path": "application/llm/groq.py",
    "chars": 497,
    "preview": "from application.core.settings import settings\nfrom application.llm.openai import OpenAILLM\n\nGROQ_BASE_URL = \"https://ap"
  },
  {
    "path": "application/llm/handlers/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "application/llm/handlers/base.py",
    "chars": 38606,
    "preview": "import logging\nimport uuid\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass\nfrom typing import Any,"
  },
  {
    "path": "application/llm/handlers/google.py",
    "chars": 3423,
    "preview": "import uuid\nfrom typing import Any, Dict, Generator\n\nfrom application.llm.handlers.base import LLMHandler, LLMResponse, "
  },
  {
    "path": "application/llm/handlers/handler_creator.py",
    "chars": 607,
    "preview": "from application.llm.handlers.base import LLMHandler\nfrom application.llm.handlers.google import GoogleLLMHandler\nfrom a"
  },
  {
    "path": "application/llm/handlers/openai.py",
    "chars": 1934,
    "preview": "from typing import Any, Dict, Generator\n\nfrom application.llm.handlers.base import LLMHandler, LLMResponse, ToolCall\n\n\nc"
  },
  {
    "path": "application/llm/llama_cpp.py",
    "chars": 2110,
    "preview": "from application.llm.base import BaseLLM\nfrom application.core.settings import settings\nimport threading\n\n\nclass LlamaSi"
  },
  {
    "path": "application/llm/llm_creator.py",
    "chars": 1818,
    "preview": "import logging\n\nfrom application.llm.anthropic import AnthropicLLM\nfrom application.llm.docsgpt_provider import DocsGPTA"
  },
  {
    "path": "application/llm/novita.py",
    "chars": 479,
    "preview": "from application.core.settings import settings\nfrom application.llm.openai import OpenAILLM\n\nNOVITA_BASE_URL = \"https://"
  },
  {
    "path": "application/llm/open_router.py",
    "chars": 522,
    "preview": "from application.core.settings import settings\nfrom application.llm.openai import OpenAILLM\n\nOPEN_ROUTER_BASE_URL = \"htt"
  },
  {
    "path": "application/llm/openai.py",
    "chars": 21004,
    "preview": "import base64\nimport json\nimport logging\n\nfrom openai import OpenAI\n\nfrom application.core.settings import settings\nfrom"
  },
  {
    "path": "application/llm/premai.py",
    "chars": 1215,
    "preview": "from application.llm.base import BaseLLM\nfrom application.core.settings import settings\n\n\nclass PremAILLM(BaseLLM):\n\n   "
  },
  {
    "path": "application/llm/sagemaker.py",
    "chars": 4961,
    "preview": "from application.llm.base import BaseLLM\nfrom application.core.settings import settings\nimport json\nimport io\n\n\nclass Li"
  },
  {
    "path": "application/logging.py",
    "chars": 5204,
    "preview": "import datetime\nimport functools\nimport inspect\n\nimport logging\nimport uuid\nfrom typing import Any, Callable, Dict, Gene"
  },
  {
    "path": "application/parser/__init__.py",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "application/parser/chunking.py",
    "chars": 3475,
    "preview": "import re\nfrom typing import List, Tuple\nimport logging\nfrom application.parser.schema.base import Document\nfrom applica"
  },
  {
    "path": "application/parser/connectors/__init__.py",
    "chars": 541,
    "preview": "\"\"\"\nExternal knowledge base connectors for DocsGPT.\n\nThis module contains connectors for external knowledge bases and do"
  },
  {
    "path": "application/parser/connectors/base.py",
    "chars": 4270,
    "preview": "\"\"\"\nBase classes for external knowledge base connectors.\n\nThis module provides minimal abstract base classes that define"
  },
  {
    "path": "application/parser/connectors/connector_creator.py",
    "chars": 2654,
    "preview": "from application.parser.connectors.google_drive.loader import GoogleDriveLoader\nfrom application.parser.connectors.googl"
  },
  {
    "path": "application/parser/connectors/google_drive/__init__.py",
    "chars": 258,
    "preview": "\"\"\"\nGoogle Drive connector for DocsGPT.\n\nThis module provides authentication and document loading capabilities for Googl"
  },
  {
    "path": "application/parser/connectors/google_drive/auth.py",
    "chars": 10288,
    "preview": "import logging\nimport datetime\nfrom typing import Optional, Dict, Any\n\nfrom google.oauth2.credentials import Credentials"
  },
  {
    "path": "application/parser/connectors/google_drive/loader.py",
    "chars": 23604,
    "preview": "\"\"\"\nGoogle Drive loader for DocsGPT.\nLoads documents from Google Drive using Google Drive API.\n\"\"\"\n\nimport io\nimport log"
  },
  {
    "path": "application/parser/connectors/share_point/__init__.py",
    "chars": 259,
    "preview": "\"\"\"\nShare Point connector package for DocsGPT.\n\nThis module provides authentication and document loading capabilities fo"
  },
  {
    "path": "application/parser/connectors/share_point/auth.py",
    "chars": 5905,
    "preview": "import datetime\nimport logging\nfrom typing import Optional, Dict, Any\n\nfrom msal import ConfidentialClientApplication\n\nf"
  },
  {
    "path": "application/parser/connectors/share_point/loader.py",
    "chars": 26515,
    "preview": "\"\"\"\nSharePoint/OneDrive loader for DocsGPT.\nLoads documents from SharePoint/OneDrive using Microsoft Graph API.\n\"\"\"\n\nimp"
  },
  {
    "path": "application/parser/embedding_pipeline.py",
    "chars": 4404,
    "preview": "import os\nimport logging\nfrom typing import List, Any\nfrom retry import retry\nfrom tqdm import tqdm\nfrom application.cor"
  },
  {
    "path": "application/parser/file/__init__.py",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "application/parser/file/audio_parser.py",
    "chars": 1695,
    "preview": "from pathlib import Path\nfrom typing import Dict, Union\n\nfrom application.core.settings import settings\nfrom application"
  },
  {
    "path": "application/parser/file/base.py",
    "chars": 654,
    "preview": "\"\"\"Base reader class.\"\"\"\nfrom abc import abstractmethod\nfrom typing import Any, List\n\nfrom langchain_core.documents impo"
  },
  {
    "path": "application/parser/file/base_parser.py",
    "chars": 1285,
    "preview": "\"\"\"Base parser and config class.\"\"\"\n\nfrom abc import abstractmethod\nfrom pathlib import Path\nfrom typing import Dict, Li"
  },
  {
    "path": "application/parser/file/bulk.py",
    "chars": 13819,
    "preview": "\"\"\"Simple reader that reads files of different formats from a directory.\"\"\"\nimport logging\nfrom pathlib import Path\nfrom"
  },
  {
    "path": "application/parser/file/constants.py",
    "chars": 545,
    "preview": "\"\"\"Shared file-extension constants for parsing and ingestion flows.\"\"\"\n\nfrom application.stt.constants import SUPPORTED_"
  },
  {
    "path": "application/parser/file/docling_parser.py",
    "chars": 11188,
    "preview": "\"\"\"Docling parser.\n\nUses docling library for advanced document parsing with layout detection,\ntable structure recognitio"
  },
  {
    "path": "application/parser/file/docs_parser.py",
    "chars": 2017,
    "preview": "\"\"\"Docs parser.\n\nContains parsers for docx, pdf files.\n\n\"\"\"\nfrom pathlib import Path\nfrom typing import Dict\n\nfrom appli"
  },
  {
    "path": "application/parser/file/epub_parser.py",
    "chars": 1208,
    "preview": "\"\"\"Epub parser.\n\nContains parsers for epub files.\n\"\"\"\n\nfrom pathlib import Path\nfrom typing import Dict\n\nfrom applicatio"
  },
  {
    "path": "application/parser/file/html_parser.py",
    "chars": 559,
    "preview": "\"\"\"HTML parser.\n\nContains parser for html files.\n\n\"\"\"\nfrom pathlib import Path\nfrom typing import Dict, Union\n\nfrom appl"
  },
  {
    "path": "application/parser/file/image_parser.py",
    "chars": 916,
    "preview": "\"\"\"Image parser.\n\nContains parser for .png, .jpg, .jpeg files.\n\n\"\"\"\nfrom pathlib import Path\nimport requests\nfrom typing"
  },
  {
    "path": "application/parser/file/json_parser.py",
    "chars": 1782,
    "preview": "import json\nfrom typing import Any, Dict, List, Union\nfrom pathlib import Path\n\nfrom application.parser.file.base_parser"
  },
  {
    "path": "application/parser/file/markdown_parser.py",
    "chars": 5180,
    "preview": "\"\"\"Markdown parser.\n\nContains parser for md files.\n\n\"\"\"\nimport re\nfrom pathlib import Path\nfrom typing import Any, Dict,"
  },
  {
    "path": "application/parser/file/openapi3_parser.py",
    "chars": 1608,
    "preview": "from urllib.parse import urlparse\n\nfrom openapi_parser import parse\n\ntry:\n    from application.parser.file.base_parser i"
  },
  {
    "path": "application/parser/file/pptx_parser.py",
    "chars": 2654,
    "preview": "\"\"\"PPT parser.\nContains parsers for presentation (.pptx) files to extract slide text.\n\"\"\"\nfrom pathlib import Path\nfrom "
  },
  {
    "path": "application/parser/file/rst_parser.py",
    "chars": 7465,
    "preview": "\"\"\"reStructuredText parser.\n\nContains parser for md files.\n\n\"\"\"\nimport re\nfrom pathlib import Path\nfrom typing import An"
  },
  {
    "path": "application/parser/file/tabular_parser.py",
    "chars": 7687,
    "preview": "\"\"\"Tabular parser.\n\nContains parsers for tabular data files.\n\n\"\"\"\nfrom pathlib import Path\nfrom typing import Any, Dict,"
  },
  {
    "path": "application/parser/remote/base.py",
    "chars": 654,
    "preview": "\"\"\"Base reader class.\"\"\"\nfrom abc import abstractmethod\nfrom typing import Any, List\n\nfrom langchain_core.documents impo"
  },
  {
    "path": "application/parser/remote/crawler_loader.py",
    "chars": 3936,
    "preview": "import logging\nimport os\nimport requests\nfrom urllib.parse import urlparse, urljoin\nfrom bs4 import BeautifulSoup\nfrom a"
  },
  {
    "path": "application/parser/remote/crawler_markdown.py",
    "chars": 6931,
    "preview": "import requests\nfrom urllib.parse import urlparse, urljoin\nfrom bs4 import BeautifulSoup\nfrom application.parser.remote."
  },
  {
    "path": "application/parser/remote/github_loader.py",
    "chars": 6623,
    "preview": "import base64\nimport requests\nimport time\nfrom typing import List, Optional\nfrom application.parser.remote.base import B"
  },
  {
    "path": "application/parser/remote/reddit_loader.py",
    "chars": 1420,
    "preview": "from application.parser.remote.base import BaseRemote\nfrom langchain_community.document_loaders import RedditPostsLoader"
  },
  {
    "path": "application/parser/remote/remote_creator.py",
    "chars": 1192,
    "preview": "from application.parser.remote.sitemap_loader import SitemapLoader\nfrom application.parser.remote.crawler_loader import "
  },
  {
    "path": "application/parser/remote/s3_loader.py",
    "chars": 14953,
    "preview": "import json\nimport logging\nimport os\nimport tempfile\nimport mimetypes\nfrom typing import List, Optional\nfrom application"
  },
  {
    "path": "application/parser/remote/sitemap_loader.py",
    "chars": 3539,
    "preview": "import logging\nimport requests\nimport re  # Import regular expression library\nimport defusedxml.ElementTree as ET\nfrom a"
  },
  {
    "path": "application/parser/remote/telegram.py",
    "chars": 348,
    "preview": "from langchain.document_loader import TelegramChatApiLoader\nfrom application.parser.remote.base import BaseRemote\n\nclass"
  },
  {
    "path": "application/parser/remote/web_loader.py",
    "chars": 1494,
    "preview": "import logging\nfrom application.parser.remote.base import BaseRemote\nfrom application.parser.schema.base import Document"
  },
  {
    "path": "application/parser/schema/__init__.py",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "application/parser/schema/base.py",
    "chars": 1011,
    "preview": "\"\"\"Base schema for readers.\"\"\"\nfrom dataclasses import dataclass\n\nfrom langchain_core.documents import Document as LCDoc"
  },
  {
    "path": "application/parser/schema/schema.py",
    "chars": 1657,
    "preview": "\"\"\"Base schema for data structures.\"\"\"\nfrom abc import abstractmethod\nfrom dataclasses import dataclass\nfrom typing impo"
  },
  {
    "path": "application/prompts/chat_combine_creative.txt",
    "chars": 898,
    "preview": "You are a helpful AI assistant, DocsGPT. You are proactive and helpful. Try to use tools, if they are available to you, "
  },
  {
    "path": "application/prompts/chat_combine_default.txt",
    "chars": 837,
    "preview": "You are a helpful AI assistant, DocsGPT. You are proactive and helpful. Try to use tools, if they are available to you, "
  },
  {
    "path": "application/prompts/chat_combine_strict.txt",
    "chars": 1039,
    "preview": "You are a helpful AI assistant, DocsGPT. You are proactive and helpful. Try to use tools, if they are available to you, "
  },
  {
    "path": "application/prompts/chat_reduce_prompt.txt",
    "chars": 151,
    "preview": "Use the following pieces of context to help answer the users question. If its not relevant to the question, respond with"
  }
]

// ... and 442 more files (download for full content)

About this extraction

This page contains the full source code of the arc53/DocsGPT GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 642 files (4.1 MB), approximately 1.1M tokens, and a symbol index with 3205 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!