Open WebUI项目源码学习记录(从0开始基于纯CPU环境部署一个网页Chat服务)
感谢您点开这篇文章:D,鼠鼠我是一个代码小白,下文是学习开源项目Open WebUI过程中的一点笔记记录,希望能帮助到你~
本人菜鸟,持续成长,能力不足有疏漏的地方欢迎一起探讨指正,比心心~
通过本文,您可以了解:
-
Open WebUI项目的基本信息和架构
-
通过ollama部署大模型、通过docker镜像和源码运行Open WebUI项目的方法
-
项目后端代码在多情景(普通提问、联网搜索提问、上传PDF文件且联网提问、上传PDF文件非联网提问)下的相关代码实现逻辑
-
RAG模块实现逻辑流程
目录
一、项目基本信息
二、运行项目源码
1、通过ollama部署大模型
1.1、安装ollma
1.2、配置ollama
1.3、下载模型
1.4、运行服务
命令行直接对话
REST API
2、搭建Open WebUI
2.1、通过docker部署
2.2、通过源码构建
编辑
三、项目结构
1、backend目录(后端代码)
1.1、start.sh
1.2、data目录
1.3、open_webui目录
1.3.1、main.py
中间件(应用于FastAPI应用中)
Task Endpoints
Pipelines Endpoints
Config Endpoints
OAuth Login & Callback
1.3.2、apps目录
1.3.2.1、webui/main.py
1.3.2.2、webui/models(重点,数据库实体)
1.3.2.3、openai/main.py
1.3.2.4、openai/chat_interceptor
1.3.3、retrieval目录
main.py
utils.py
2、src目录(前端代码)
四、特定情景下代码链路逻辑
情景1:用户在界面发送消息时,代码调用逻辑:
情景2:用户进行联网搜索提问“武汉今天天气如何”时,代码调用逻辑:
情景3.1:用户上传PDF文件,让其帮忙总结(联网搜索功能关闭),代码逻辑:
情景3.2:用户上传PDF文件,让其帮忙总结,(联网搜索功能开启),代码逻辑:
五、总结
一、项目基本信息
-
Github:https://github.com/open-webui/open-webui
-
官方文档:https://docs.openwebui.com/getting-started/
-
代码版本:v0.3.32(2024.10.6)——本文学习版本,目前最新版本已更新至v0.3.35(截至2024.10.28)
二、运行项目源码
作者本地环境:Ubuntu24.04,纯CPU
通过ollama部署大模型qwen2:7b作为模型端,通过Open WebUI提供用户chat服务。
1、通过ollama部署大模型
ollama是大模型部署方案,对应docker,本质也是基于docker的容器化技术
1.1、安装ollma
官方地址:https://ollama.com/
开源地址:https://github.com/ollama/ollama
打开官网,点击Downloard,根据操作系统选择对应下载方式。
以Ubuntu24.04为例,通过下述命令下载:
curl -fsSL https://ollama.com/install.sh | sh#下载完成后查询版本信息
ollama -v#查看状态
如上,ollama已经成功安装。
1.2、配置ollama
通过编辑ollama.service进行配置:
sudo vim /etc/systemd/system/ollama.service
-
更改HOST
由于Ollama的默认参数配置,启动时设置了仅本地访问,因此需要对HOST进行配置,开启监听任何来源IP。
[Service]
# 配置远程访问
Environment="OLLAMA_HOST=0.0.0.0"
-
更改模型存储路径
默认情况下,不同操作系统大模型存储的路径如下:
macOS: ~/.ollama/modelsLinux: /usr/share/ollama/.ollama/modelsWindows: C:\Users.ollama\models
如果要修改模型文件的存储路径,设置如下:
[Service]
# 配置OLLAMA的模型存放路径
Environment="OLLAMA_MODELS=/data/ollama/models"
如果因为指定的目录ollama用户及用户组没有相应权限,导致服务不能启动。可以通过授权给相应的目录权限解决问题:
chown ollama:ollama ollama/models
-
应用配置
重载systemd并重启Ollama
systemctl daemon-reload
systemctl restart ollama
配置完成后,访问测试。浏览器访问http://IP:11434/,出现Ollama is running代表成功。
1.3、下载模型
ollama的命令和docker操作命令非常相似。可通过shell窗口输入ollama查看相关命令:
******:~/work# ollama
Usage:ollama [flags]ollama [command]Available Commands:serve Start ollamacreate Create a model from a Modelfileshow Show information for a modelrun Run a modelpull Pull a model from a registrypush Push a model to a registrylist List modelsps List running modelscp Copy a modelrm Remove a modelhelp Help about any commandFlags:-h, --help help for ollama-v, --version Show version informationUse "ollama [command] --help" for more information about a command.
由上可知ollama相关命令:
ollama serve # 启动ollama
ollama create # 从模型文件创建模型
ollama show # 显示模型信息
ollama run # 运行模型
ollama pull # 从注册仓库中拉取模型
ollama push # 将模型推送到注册仓库
ollama list # 列出已下载模型
ollama cp # 复制模型
ollama rm # 删除模型
ollama help # 获取有关任何命令的帮助信息
-
拉取qwen2-7b模型
ollama pull qwen2:7b#下载成功查看模型
ollama list
可见,已成功拉取:
也可以自定义模型,所谓自定义模型就是不适用Ollama官方模型库中的模型,理论可以使用其他各类经过转换处理的模型,有从GGUF导入和从PyTorch或Safetensors导入两种方式。
所谓从从PyTorch或Safetensors导入Ollama,其实就是使用llama.cpp项目,对PyTorch或Safetensors类型的模型进行转换、量化处理成GGUF格式的模型,然后再用Ollama加载使用 。
参考:Ollama:一个在本地部署、运行大型语言模型的工具-CSDN博客
-
运行模型
运行模型并进行对话:
ollama run qwen2:7b
1.4、运行服务
命令行直接对话
如上,运行模型可以直接与模型进行对话。
REST API
运行模型后,执行ollama serve命令启动Ollama服务,然后就可以通过API形式进行模型调用。ollama serve会自动启动一个http服务,可以通过http请求模型服务。
参考官方API文档:https://github.com/ollama/ollama/blob/main/docs/api.md
生成回复
curl http://localhost:11434/api/generate -d '{"model": "qwen2:7b","prompt":"你是谁?为什么天空是蓝色的?"
}'
上述localhost也可以换成ip。
若要禁用流式,如下操作:
curl http://ip:11434/api/generate -d '{"model": "qwen2:7b","prompt":"你是谁?为什么天空是蓝色的?","stream":false
}'
与模型聊天
curl http://localhost:11434/api/chat -d '{"model": "qwen2:7b","messages": [{ "role": "user", "content": "天空为什么是蓝色的?" }]
}'
也可以带历史记录
curl http://localhost:11434/api/chat -d '{"model": "qwen2:7b","messages": [{"role": "user","content": "why is the sky blue?"},{"role": "assistant","content": "due to rayleigh scattering."},{"role": "user","content": "how is that different than mie scattering?"}]
}'
2、搭建Open WebUI
Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 WebUI,旨在完全离线操作。它支持各种 LLM 运行程序,包括 Ollama 和 OpenAI 兼容的 API。
-
Github:https://github.com/open-webui/open-webui
-
Open WebUI:https://docs.openwebui.com/
-
社区:https://openwebui.com/
2.1、通过docker部署
使用Docker部署安装Open WebUI。计算机已有ollama,使用以下命令:
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main
访问http://IP:3000,创建一个账号(管理员)
登陆账号:
进入Open WebUI后,界面如下。在Settings中进行相关设置
管理员设置,设置外部连接:
设置连接后,在选择模型部分可见上面下载下来的千问模型:
选择模型即可进行对话:
2.2、通过源码构建
也可以通过本地运行项目源码进行搭建。(以Linux为例)
# Copying required .env file
cp -RPp .env.example .env# Building Frontend Using Node
npm install
npm run buildcd ./backend# Optional: To install using Conda as your development environment, follow these instructions:
# Create and activate a Conda environment
conda create --name open-webui-env python=3.11
conda activate open-webui-env# Install dependencies
pip install -r requirements.txt -U# Start the application
bash start.sh
在鼠鼠我多次构建的过程中,有次有遇到一个错误,报错如下:
(venv) ******:~/PycharmProjects/openwebui(v0.3.32)/open-webui$ npm run build> open-webui@0.3.32 build
> npm run pyodide:fetch && vite build> open-webui@0.3.32 pyodide:fetch
> node scripts/prepare-pyodide.jsSetting up pyodide + micropip
Failed to load Pyodide: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/home/***/PycharmProjects/openwebuiv0.3.32/open-webui/node_modules/pyodide/pyodide.asm.js' imported from /home/***/PycharmProjects/openwebui(v0.3.32)/open-webui/node_modules/pyodide/pyodide.mjsat new NodeError (node:internal/errors:405:5)at finalizeResolution (node:internal/modules/esm/resolve:327:11)at moduleResolve (node:internal/modules/esm/resolve:980:10)at defaultResolve (node:internal/modules/esm/resolve:1193:11)at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:403:12)at ModuleLoader.resolve (node:internal/modules/esm/loader:372:25)at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:249:38)at ModuleLoader.import (node:internal/modules/esm/loader:335:34)at importModuleDynamically (node:internal/modules/esm/translators:143:35)at importModuleDynamicallyCallback (node:internal/modules/esm/utils:112:14) {url: 'file:///home/***/PycharmProjects/openwebuiv0.3.32/open-webui/node_modules/pyodide/pyodide.asm.js',code: 'ERR_MODULE_NOT_FOUND'
}
Copying Pyodide files into static directory
node:internal/process/promises:288triggerUncaughtException(err, true /* fromPromise */);^[Error: ENOENT: no such file or directory, open '/home/***/PycharmProjects/openwebuiv0.3.32/open-webui/node_modules/pyodide/python_stdlib.zip'] {errno: -2,code: 'ENOENT',syscall: 'open',path: '/home/***/PycharmProjects/openwebuiv0.3.32/open-webui/node_modules/pyodide/python_stdlib.zip'
}Node.js v18.19.1
原因:涉及两方面,一是node.js 版本问题,如下图(官方最新文档要求),目前版本低于要求版本。
另一个方面——文件夹命名问题。原来的项目处于的一个文件夹为“openwebui(v0.3.32)”,其中包含括号和".",在后续代码执行中,由于路径中包含特殊字符(在这个案例中是括号 (
和 )
),导致命令解释错误或者文件系统路径解析出现问题。在文件或目录名称中使用特殊字符,如括号、星号、问号、波浪线等,经常会导致这类问题,因为这些字符在 Unix 和 Linux 命令行中可能有特殊含义。
解决方法:将项目所处的目录进行重命名为“openwebui_v0_3_32”,即可解决。
Q:点击“+”功能只有“上传文件”,没有“联网搜索”,如何解决?
A:需要管理员在面板中进行设置搜索引擎,本质上是通过api调用:
三、项目结构
整个代码语言构成分布如下,其中,Svelte 是一种现代的前端框架,用于构建高性能的Web应用程序。
项目文件如下所示,主要分为前端、后端、测试和部署脚本:
-
backend目录:后端代码目录,包含API服务、数据库操作等
-
cypress 目录:包含Cypress测试框架的配置和测试脚本,用于端到端测试
-
docs 目录:文档目录,包含项目说明、安全指南等。
-
kubernetes : 包含Kubernetes部署配置文件。
-
scripts : 包含各种脚本文件,用于自动化部署、测试或其他任务的脚本。
-
src :前端代码目录,存放Svelte组件和相关资源的地方。
-
static : 静态文件目录,如图片、CSS、客户端JavaScript等。
-
test/test_files/image_gen : 测试目录下的子目录,包含用于测试的图像生成器。
1、backend目录(后端代码)
-
data文件夹:用于存储后端服务需要的数据文件,如数据库、文档等
-
open-webui文件夹:包含后端服务的主要代码和配置文件
-
dev.sh:用于本地开发环境的启动脚本
-
start.sh 和 start_windows.bat - 用于启动后端服务的脚本,分别适用于类Unix系统和Windows系统。
1.1、start.sh
启动脚本,最后会启动一个 Uvicorn 服务器,并通过这个命令来运行 open-webui/backend/open_webui/main.py 文件中的FastAPI的 app 应用对象,监听在指定的主机和端口上,并允许所有的转发 IP 地址。
1.2、data目录
-
cache - 用于存储应用程序的缓存数据。
-
functions - 包含一些后端服务使用的函数或脚本。
-
tools - 包含一些用于后端服务的工具或脚本。
-
uploads - 用于存储用户上传的文件。
-
vector_db - 用于存储向量数据库或类似的数据结构。
-
readme.txt - 包含文件夹的说明或使用指南。
-
webui.db - 后端服务使用的数据库文件。
1.3、open_webui目录
-
apps - 包含后端服务的应用程序逻辑。
-
data - 与主
data
文件夹类似,用于存储后端服务需要的数据文件。 -
migrations - 包含数据库迁移脚本,用于数据库结构的版本控制。
-
static - 包含静态文件,如图片、CSS、JavaScript等。
-
test - 包含测试代码和测试用例。
-
utils - 包含一些后端服务使用的实用工具或函数。
-
init.py - Python模块初始化文件。
-
alembic.ini - Alembic数据库迁移工具的配置文件。
-
config.py - 后端服务的配置文件。
-
constants.py - 包含后端服务使用的常量。
-
env.py - 包含环境变量的配置。
-
main.py - 是后端服务的入口点或主程序。
1.3.1、main.py
中间件(应用于FastAPI应用中)
- ChatCompletionMiddleware类 :用于处理与聊天补全相关的请求,包括模型选择、过滤函数、工具函数调用和文件处理。
-
PipelineMiddleware类:对请求进行预处理和后处理。处理管道中的过滤器调用。
-
也添加中间件CORSMiddleware、SecurityHeadersMiddleware 以及 PipelineMiddleware 本身,分别负责处理跨域资源共享(CORS)、安全头部设置以及自定义的业务逻辑处理。
设置相关路由
-
@app.get("/api/models") : 用于获取模型的列表
-
@app.post("/api/chat/completed"):完整的聊天补全请求处理流程,包括模型验证、外部API调用、事件处理、全局和本地过滤器调用。
-
@app.post("/api/chat/actions/{action_id}"):用于处理特定动作的请求。它通过执行与动作ID关联的功能来响应聊天中的动作请求
Task Endpoints
路由 | 作用 |
---|---|
GET请求端点/api/task/config | 它返回当前应用的状态配置信息 |
POST请求端点/api/task/config/update | 用于更新任务配置。只有管理员用户可以访问此端点,并且需要提供一个符合TaskConfigForm模型的JSON数据体来进行更新操作。 |
POST请求端点/api/task/title/completions | 用于根据给定的提示生成标题 |
POST请求端点/api/task/query/completions | 用于根据用户的对话历史生成搜索查询 |
POST请求端点/api/task/emoji/completions | 用于根据文本内容生成相应的表情符号 |
POST请求端点/api/task/moa/completions | 用于综合多个模型的响应生成最终答案 |
Pipelines Endpoints
@app.get("/api/pipelines/list"):通过调用get_openai_models函数获取模型列表,然后筛选出包含“pipelines”字段的响应,并返回相应的API URL和索引
Config Endpoints
@app.post("/api/pipelines/upload"):允许用户上传Python脚本文件作为管道。
接下来的几个段落分别定义了添加、删除管道以及获取管道详情等的端点;
并且定义了一系列API端点,用于管理和获取应用程序的配置信息。
OAuth Login & Callback
实现完整的OAuth登录和注册流程,包括客户端注册、会话管理、用户认证和JWT令牌生成等功能。
1.3.2、apps目录
1.3.2.1、webui/main.py
注册了多个路由处理器,处理不同类型api请求,如用户认证、文件上传、模型管理等。
定义相关核心函数:
-
get_status:根路由处理函数,返回应用的状态信息。
-
get_function_module:根据管道ID加载函数模块。
-
get_pipe_models:获取管道模型的详细信息。
-
execute_pipe:执行管道函数。
-
get_message_content:从不同的响应类型中获取消息内容。
-
process_line:处理聊天消息的每一行。
-
get_pipe_id:从表单数据中获取管道ID。
-
get_function_params:获取函数参数。
最后定义函数generate_function_chat_completion,实现聊天补全处理相关逻辑。
1.3.2.2、webui/models(重点,数据库实体)
(未完待续。。。。。。待整理)
1.3.2.3、openai/main.py
-
设置FastAPI应用、Middleware和依赖注入(中间件会在每次请求前执行,确保在访问模型端点之前已经加载了模型数据)。
-
设置api路由:
-
/config:提供了一个GET方法来返回当前的应用程序配置,包括是否启用OpenAI API的功能。
-
/config/update:接受一个POST请求,更新应用程序的配置,特别是启用或禁用OpenAI API的功能。
-
/urls和/keys:分别提供了GET方法来显示当前的OpenAI API URLs和Keys列表,以及POST方法来更新这些列表。
-
/audio/speech:这是一个音频处理的端点,接受用户的语音输入并生成对应的音频文件响应。
-
-
设置异步函数,例如fetch_url, cleanup_response, merge_models_lists, get_all_models_raw, get_all_models等。这些函数主要负责与外部API通信、处理JSON数据、合并模型列表等工作。
1.3.2.4、openai/chat_interceptor
实现一个简单的聊天系统拦截器,可以用于检查和处理特定的情况,例如不支持的URL或过长的上下文文本。
模块 | 实现 |
解析用户输入 | get_message_text函数:从用户输入中提取文本内容 |
生成聊天响应 | generate_chat_response函数:生成聊天响应,包括生成一个唯一的ID、创建时间、模型名称、选择内容和使用情况 |
拦截器列表 | 包含了一系列的拦截器实例 chat_interceptor_before_lark_doc_content和 chat_interceptor_after_lark_doc_content: 分别在处理飞书文档内容之前和之后使用的拦截器列表。 |
拦截器入口 | 遍历拦截器列表,并调用每个拦截器的 intercept_chat_completion_before_lark_doc_content和intercept_chat_completion_after_lark_doc_content: 分别是在处理飞书文档内容之前和之后调用的拦截器入口函数。 |
拦截器类型 |
检查用户输入中是否包含不支持的URL,如果是,则返回默认回答。
|
拦截器调用 | 在发送聊天请求之前或之后,调用拦截器列表中的拦截器,每个拦截器都会检查请求,并决定是否拦截请求。 |
1.3.3、retrieval目录
-
loaders:从各种来源加载和处理文档内容,适用于需要跨多种文件格式工作的应用场景。
-
models:定义了用于检索任务的模型
-
vector:
-
包含与向量相关的文件,如
dbs
和connector.py
,用于处理向量数据库的连接和交互,以及向量化文本数据以用于相似性搜索。 -
main.py
文件可能包含与向量检索相关的主要逻辑。
-
-
web:
-
包含多个与Web相关的Python文件,如
brave.py
、duckduckgo.py
等,这些文件用于实现与不同搜索引擎(如Brave Search、DuckDuckGo)的交互,以便从这些搜索引擎获取数据。 -
main.py
和utils.py
文件可能包含Web应用的主要逻辑和辅助功能。 -
testdata
目录可能包含用于测试的示例数据。
-
-
utils.py:一个通用的工具文件,包含在整个应用中使用的辅助函数和类。
-
main.py:后端服务入口点,主要用于处理文档检索和向量数据库操作。
(下面的内容是旧版本v0.3.21中rag目录,即对应v0.3.32中retrieval目录,两版本肯定有差异,下面是之前学习旧版本的笔记,仅供参考)
main.py
配置和模型更新
-
定义
update_embedding_model
和update_reranking_model
函数来更新嵌入和重排模型。 -
使用
get_embedding_function
获取嵌入函数,用于将文本转换为向量表示
API 路由和处理函数
-
定义了多个 API 路由和处理函数,例如:
-
/
:根路由,返回应用状态。 -
/embedding
:返回嵌入模型的配置。 -
/reranking
:返回重排模型的配置。 -
/embedding/update
和/reranking/update
:更新嵌入和重排模型的配置。 -
/config
:返回 RAG 应用的配置。 -
/config/update
:更新 RAG 应用的配置。 -
/template
和/query/settings
:获取和更新查询模板和设置。 -
/query/doc
和/query/collection
:处理文档和集合的查询请求。 -
/youtube
和/web
:处理 YouTube 视频和网页内容的存储请求。 -
/web/search
:处理网页搜索请求。
-
文档和网页处理
-
定义了
get_loader
函数,根据文件类型选择适当的加载器(如 TikaLoader、TextLoader 等)。 -
定义了
store_data_in_vector_db
和store_text_in_vector_db
函数,用于将数据存储到向量数据库中。
错误处理:
-
使用
HTTPException
处理错误情况,并返回错误信息。
辅助函数:
-
定义
get_web_loader
、validate_url
、resolve_hostname
等辅助函数,用于加载和验证网页内容。
搜索功能:
-
定义
search_web
函数,用于通过不同的搜索引擎进行搜索。
安全加载器:
-
定义
SafeWebBaseLoader
类,用于增强错误处理,确保即使某些 URL 无法访问,系统仍然可以正常工作。
rag模块的作用流程:
utils.py
处理检索增强生成(RAG)任务的函数,主要涉及从不同数据源中提取和查询信息
-
query_doc 函数:用于从一个指定的集合中查询与给定查询最相关的文档。
-
query_doc_with_hybrid_search 函数:扩展了基本的查询功能,引入了混合搜索的概念。它不仅使用BM25Retriever进行初步筛选,还结合了ChromaRetriever进行更精确的搜索,并通过EnsembleRetriever组合两者的结果。此外,它还包括一个重排序步骤,通过RerankCompressor对结果进行进一步优化。
-
merge_and_sort_query_results 函数:用于合并多个查询结果,并对它们按相关性进行排序。它会将所有结果的距离、文档和元数据合并在一起,然后根据距离进行降序或升序排列,最后只保留前K个结果。
-
query_collection 和 query_collection_with_hybrid_search 函数:这两个函数分别实现了基于普通搜索和混合搜索的多集合查询。它们遍历一组集合名称,对每个集合执行相应的查询操作,并将结果合并和排序后返回。
-
rag_template 函数:用于替换模板字符串中的占位符,以便在生成的上下文中插入具体的查询和上下文内容。
-
get_embedding_function 函数:根据不同的嵌入引擎和模型生成对应的嵌入函数。
-
get_rag_context 函数:从文件列表和消息记录中提取与当前查询最相关的上下文。
-
get_model_path 函数:用于确定Hugging Face模型的本地路径。
-
generate_openai_embeddings 和 generate_openai_batch_embeddings 函数:用于调用OpenAI API生成文本的嵌入向量。前者处理单个文本输入,后者则可以处理一批文本输入,适用于批量处理的场景。
-
ChromaRetriever 类和 RerankCompressor 类:分别是LangChain库中原有的Retriever和DocumentCompressor的具体实现。ChromaRetriever负责从Chroma数据库中检索文档,而RerankCompressor则在检索到的文档基础上进行进一步的重排序。
。。。。。。(未完待续,鼠鼠后面有空会继续更新的惹)
2、src目录(前端代码)
-
lib:包含可重用的JavaScript或Svelte组件、工具函数、实用程序等
-
routes:包含Svelte路由文件,用于定义应用程序的页面路由。
-
app.css:包含全局样式表,定义了样式重置、通用样式或主题。
-
app.d.ts:TypeScript的声明文件,用于为项目提供类型定义。
-
app.html:项目的HTML模板文件,通常是应用程序的入口点。
-
tailwind.css:使用Tailwind CSS时的全局样式文件。
(由于前端不是鼠鼠我学习的重点,所以没在看前端部分了)
四、特定情景下代码链路逻辑
情景1:用户在界面发送消息时,代码调用逻辑:
核心部分:
1、构建prompt和调用大模型:
"""
[open_webui.apps.ollama.main]
"""@app.post("/api/chat/{url_idx}")
async def generate_chat_completion(form_data: GenerateChatCompletionForm,url_idx: Optional[int] = None,user=Depends(get_verified_user),
):log.info(f"/api/chat或/api/chat/{url_idx}")payload = {**form_data.model_dump(exclude_none=True)}log.debug(f"{payload = }")if "metadata" in payload:del payload["metadata"]model_id = form_data.modelif app.state.config.ENABLE_MODEL_FILTER:if user.role == "user" and model_id not in app.state.config.MODEL_FILTER_LIST:raise HTTPException(status_code=403,detail="Model not found",)model_info = Models.get_model_by_id(model_id)if model_info:if model_info.base_model_id:payload["model"] = model_info.base_model_idparams = model_info.params.model_dump()if params:if payload.get("options") is None:payload["options"] = {}payload["options"] = apply_model_params_to_body_ollama(params, payload["options"])#构建promptpayload = apply_model_system_prompt_to_body(params, payload, user)if ":" not in payload["model"]:payload["model"] = f"{payload['model']}:latest"url = get_ollama_url(url_idx, payload["model"])log.info(f"url: {url}")log.debug(payload)#调用大模型return await post_streaming_url(f"{url}/api/chat",json.dumps(payload),stream=form_data.stream,content_type="application/x-ndjson",)
构建prompt的apply_model_system_prompt_to_body函数细节:
"""
backend/open_webui/utils/payload.py
"""# inplace function: form_data is modified
def apply_model_system_prompt_to_body(params: dict, form_data: dict, user) -> dict:system = params.get("system", None)if not system:return form_dataif user:template_params = {"user_name": user.name,"user_location": user.info.get("location") if user.info else None,}else:template_params = {}system = prompt_template(system, **template_params)form_data["messages"] = add_or_update_system_message(system, form_data.get("messages", []))return form_data
调用大模型的post_streaming_url函数细节:
"""
backend/open_webui/apps/ollama/main.py
"""async def post_streaming_url(url: str, payload: Union[str, bytes], stream: bool = True, content_type=None
):log.info("post_streaming_url")r = Nonetry:session = aiohttp.ClientSession(trust_env=True, timeout=aiohttp.ClientTimeout(total=AIOHTTP_CLIENT_TIMEOUT))r = await session.post(url,data=payload,headers={"Content-Type": "application/json"},)r.raise_for_status()if stream:headers = dict(r.headers)if content_type:headers["Content-Type"] = content_typereturn StreamingResponse(r.content,status_code=r.status,headers=headers,background=BackgroundTask(cleanup_response, response=r, session=session),)else:res = await r.json()await cleanup_response(r, session)return resexcept Exception as e:error_detail = "Open WebUI: Server Connection Error"if r is not None:try:res = await r.json()if "error" in res:error_detail = f"Ollama: {res['error']}"except Exception:error_detail = f"Ollama: {e}"raise HTTPException(status_code=r.status if r else 500,detail=error_detail,)
情景2:用户进行联网搜索提问“武汉今天天气如何”时,代码调用逻辑:
核心代码:
1、生成搜索查询:
"""
backend/open_webui/main.py
"""@app.post("/api/task/query/completions")
async def generate_search_query(form_data: dict, user=Depends(get_verified_user)):log.info("/api/task/query/completions")print("generate_search_query")if not app.state.config.ENABLE_SEARCH_QUERY:raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=f"Search query generation is disabled",)model_id = form_data["model"]if model_id not in app.state.MODELS:raise HTTPException(status_code=status.HTTP_404_NOT_FOUND,detail="Model not found",)# Check if the user has a custom task model# If the user has a custom task model, use that modeltask_model_id = get_task_model_id(model_id)print(task_model_id)model = app.state.MODELS[task_model_id]if app.state.config.SEARCH_QUERY_GENERATION_PROMPT_TEMPLATE != "":template = app.state.config.SEARCH_QUERY_GENERATION_PROMPT_TEMPLATEelse:template = """Given the user's message and interaction history, decide if a web search is necessary. You must be concise and exclusively provide a search query if one is necessary. Refrain from verbose responses or any additional commentary. Prefer suggesting a search if uncertain to provide comprehensive or updated information. If a search isn't needed at all, respond with an empty string. Default to a search query when in doubt. Today's date is {{CURRENT_DATE}}.User Message:
{{prompt:end:4000}}Interaction History:
{{MESSAGES:END:6}}Search Query:"""content = search_query_generation_template(template, form_data["messages"], {"name": user.name})print("content", content)payload = {"model": task_model_id,"messages": [{"role": "user", "content": content}],"stream": False,**({"max_tokens": 30}if app.state.MODELS[task_model_id]["owned_by"] == "ollama"else {"max_completion_tokens": 30,}),"metadata": {"task": str(TASKS.QUERY_GENERATION), "task_body": form_data},}log.debug(payload)# Handle pipeline filterstry:payload = filter_pipeline(payload, user)except Exception as e:if len(e.args) > 1:return JSONResponse(status_code=e.args[0],content={"detail": e.args[1]},)else:return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST,content={"detail": str(e)},)if "chat_id" in payload:del payload["chat_id"]return await generate_chat_completions(form_data=payload, user=user)
2、执行搜索查询
"""
backend/open_webui/apps/retrieval/main.py
"""@app.post("/process/web/search")
def process_web_search(form_data: SearchForm, user=Depends(get_verified_user)):log.info("调用函数process_web_search")try:logging.info(f"trying to web search with {app.state.config.RAG_WEB_SEARCH_ENGINE, form_data.query}")web_results = search_web(app.state.config.RAG_WEB_SEARCH_ENGINE, form_data.query)except Exception as e:log.exception(e)print(e)raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=ERROR_MESSAGES.WEB_SEARCH_ERROR(e),)try:collection_name = form_data.collection_nameif collection_name == "":collection_name = calculate_sha256_string(form_data.query)[:63]urls = [result.link for result in web_results]loader = get_web_loader(urls)docs = loader.load()save_docs_to_vector_db(docs, collection_name, overwrite=True)return {"status": True,"collection_name": collection_name,"filenames": urls,}except Exception as e:log.exception(e)raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=ERROR_MESSAGES.DEFAULT(e),)
情景3.1:用户上传PDF文件,让其帮忙总结(联网搜索功能关闭),代码逻辑:
核心代码:
1、接收处理保存用户上传的PDF文件
"""
backend/open_webui/apps/webui/routers/files.py
"""@router.post("/")
def upload_file(file: UploadFile = File(...), user=Depends(get_verified_user)):log.info("调用函数:upload_file")log.info(f"file.content_type: {file.content_type}")try:unsanitized_filename = file.filenamefilename = os.path.basename(unsanitized_filename)# replace filename with uuidid = str(uuid.uuid4())name = filenamefilename = f"{id}_{filename}"file_path = f"{UPLOAD_DIR}/{filename}"contents = file.file.read()with open(file_path, "wb") as f:f.write(contents)f.close()file = Files.insert_new_file(user.id,FileForm(**{"id": id,"filename": filename,"meta": {"name": name,"content_type": file.content_type,"size": len(contents),"path": file_path,},}),)try:process_file(ProcessFileForm(file_id=id))file = Files.get_file_by_id(id=id)except Exception as e:log.exception(e)log.error(f"Error processing file: {file.id}")if file:return fileelse:raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=ERROR_MESSAGES.DEFAULT("Error uploading file"),)except Exception as e:log.exception(e)raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=ERROR_MESSAGES.DEFAULT(e),)
2、上述调用的处理文件的函数
"""
backend/open_webui/apps/retrieval/main.py
"""@app.post("/process/file")
def process_file(form_data: ProcessFileForm,user=Depends(get_verified_user),
):log.info("调用函数:process_file")try:file = Files.get_file_by_id(form_data.file_id)collection_name = form_data.collection_nameif collection_name is None:collection_name = f"file-{file.id}"if form_data.content:# Update the content in the file# Usage: /files/{file_id}/data/content/updateVECTOR_DB_CLIENT.delete(collection_name=f"file-{file.id}",filter={"file_id": file.id},)docs = [Document(page_content=form_data.content,metadata={"name": file.meta.get("name", file.filename),"created_by": file.user_id,"file_id": file.id,**file.meta,},)]text_content = form_data.contentelif form_data.collection_name:# Check if the file has already been processed and save the content# Usage: /knowledge/{id}/file/add, /knowledge/{id}/file/updateresult = VECTOR_DB_CLIENT.query(collection_name=f"file-{file.id}", filter={"file_id": file.id})if len(result.ids[0]) > 0:docs = [Document(page_content=result.documents[0][idx],metadata=result.metadatas[0][idx],)for idx, id in enumerate(result.ids[0])]else:docs = [Document(page_content=file.data.get("content", ""),metadata={"name": file.meta.get("name", file.filename),"created_by": file.user_id,"file_id": file.id,**file.meta,},)]text_content = file.data.get("content", "")else:# Process the file and save the content# Usage: /files/file_path = file.meta.get("path", None)if file_path:loader = Loader(engine=app.state.config.CONTENT_EXTRACTION_ENGINE,TIKA_SERVER_URL=app.state.config.TIKA_SERVER_URL,PDF_EXTRACT_IMAGES=app.state.config.PDF_EXTRACT_IMAGES,)docs = loader.load(file.filename, file.meta.get("content_type"), file_path)else:docs = [Document(page_content=file.data.get("content", ""),metadata={"name": file.filename,"created_by": file.user_id,"file_id": file.id,**file.meta,},)]text_content = " ".join([doc.page_content for doc in docs])log.debug(f"text_content: {text_content}")Files.update_file_data_by_id(file.id,{"content": text_content},)hash = calculate_sha256_string(text_content)Files.update_file_hash_by_id(file.id, hash)try:result = save_docs_to_vector_db(docs=docs,collection_name=collection_name,metadata={"file_id": file.id,"name": file.meta.get("name", file.filename),"hash": hash,},add=(True if form_data.collection_name else False),)if result:Files.update_file_metadata_by_id(file.id,{"collection_name": collection_name,},)return {"status": True,"collection_name": collection_name,"filename": file.meta.get("name", file.filename),"content": text_content,}except Exception as e:raise eexcept Exception as e:log.exception(e)if "No pandoc was found" in str(e):raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=ERROR_MESSAGES.PANDOC_NOT_INSTALLED,)else:raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST,detail=str(e),)
情景3.2:用户上传PDF文件,让其帮忙总结,(联网搜索功能开启),代码逻辑:
五、总结
开源项目目前还在不断更新迭代,相信后面会有更好的功能体验。本人水平有限,有错轻喷谢谢~
相关文章:
Open WebUI项目源码学习记录(从0开始基于纯CPU环境部署一个网页Chat服务)
感谢您点开这篇文章:D,鼠鼠我是一个代码小白,下文是学习开源项目Open WebUI过程中的一点笔记记录,希望能帮助到你~ 本人菜鸟,持续成长,能力不足有疏漏的地方欢迎一起探讨指正,比心心~…...
WPF Prism 01-BootstrapperShell
Prism介绍 Prism 是一个用于在 WPF、.NET MAUI、Uno 平台和 Xamarin Forms 中构建松耦合、可维护和可测试的 XAML 应用程序的框架。每个平台都有单独的发布版本,并且这些版本将在独立的开发时间线上进行开发。Prism 提供了一组设计模式的实现,这些模式有…...
在Ubuntu22.04 jammy下用qemu模型riscv32环境装鸿蒙(未完成,待续)
在使用实体ESP32C3 安装鸿蒙失败后,就是这个:完全按照手册win10里装Ubuntu 虚拟机然后编译ESP32(主要是想针对ESP32C3和S3)开发板的鸿蒙系统(失败)-CSDN博客转向用qemu模拟环境装鸿蒙 学习手册riscv32_virt/README_zh.md OpenHar…...
第2章:CSS基本语法 --[CSS零基础入门]
CSS(层叠样式表,Cascading Style Sheets)是用来描述HTML或XML(包括各种XML:SVG, MathML 或 XHTML)等文档的外观和格式的语言。以下是CSS的基本语法: 1.选择器 1.元素选择器 元素选择器是基于HTML标签名称来选择元素的。当你使用元素选择器时,你是在告诉浏览器对页面…...
win11 恢复任务栏copilot图标, 亲测有效
1、修改C:\Windows\System32\IntegratedServicesRegionPolicySet.json,解除中国不能使用copilot的限制。 使用Notepad搜索copilot全文搜索,将下面两处的“CN,”删除,删除后如下: {"$comment": "Show Copilot on t…...
Python爬虫实战:抓取拼多多商品详情数据(基于pdd.item_get接口)
在当前的电商市场中,拼多多以其独特的拼团模式和优惠价格吸引了大量用户,成为继淘宝、京东之后的又一大电商平台。对于数据分析和市场研究者来说,获取拼多多的商品详情数据显得尤为重要。本文将介绍如何使用Python爬虫技术,通过调…...
如何在x86模拟器和鸿蒙API9如何使用MQTT模块ohos_mqtt
目录 引言 安装失败的原因 如何编译so文件的x86_64版本 下载源代码 安装NDK 代码编译 安装MQTT软件包 避免MQTT软件包自动升级 设置libs 客户端程序的编写 运行测试 结语 参考文献 引言 在上周的博客(如何在鸿蒙API9和x86模拟器中使用MQTT-CSDN博客&am…...
VB.NET 从入门到精通:开启编程进阶之路
摘要: 本文全面深入地阐述了 VB.NET 的学习路径,从基础的环境搭建与语法入门开始,逐步深入到面向对象编程、图形用户界面设计、数据访问、异常处理、多线程编程以及与其他技术的集成等核心领域,通过详细的代码示例与理论讲解&…...
芝法酱学习笔记(1.3)——SpringBoot+mybatis plus+atomikos实现多数据源事务
一、前言 1.1 业务需求 之前我们在讲解注册和登录的时候,有一个重要的技术点忽略了过去。那就是多数据源的事务问题。 按照我们的业务需求,monitor服务可能涉及同时对监控中心数据库和企业中心数据库进行操作,而我们希望这样的操作在一个事…...
图像处理插件:让小程序焕发视觉新生的秘密武器
在小程序开发中,图像处理是一个重要的环节,它涉及到图片的加载、显示、裁剪、压缩等多个方面。为了简化这一复杂过程,开发者通常会使用图像处理插件。这些插件不仅提供了丰富的图像处理功能,还封装了底层的图像操作逻辑࿰…...
力扣刷题TOP101: 27.BM34 判断是不是二叉搜索树
目录: 目的 思路 复杂度 记忆秘诀 python代码 目的: 给定一个二叉树根节点,请判断这棵树是不是二叉搜索树。 二叉搜索树满足每个节点的左子树上的所有节点均小于当前节点且右子树上的所有节点均大于当前节点。 思路 什么是二叉搜索树&am…...
Linux图形化工具推荐
1、MobaXterm MobaXterm Xserver with SSH, telnet, RDP, VNC and X11 - DownloadFree X server for Windows with tabbed SSH terminal, telnet, RDP, VNC and X11-forwarding - Downloadhttps://mobaxterm.mobatek.net/download.html 2、FinalShell FinalShell SSH工具,服…...
蓝队基础:企业网络安全架构与防御策略
声明 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,此文章为对视频内容稍加整理发布,文章所提到的网站以及内容,只做学习交流,其他…...
数据结构:栈
什么是栈: 栈是一种特殊的线性表,仅能在线性表的一端操作,栈顶允许操作,栈底不允许操作。 栈的特点是:先进后出,或者说是后进先出,从栈顶放入元素的操作叫入栈,取出元素叫出栈。 栈…...
tcp_recvmsg 函数
tcp_recvmsg 函数是 Linux 内核 TCP 栈的一部分,它主要用于处理从 TCP socket 接收数据的过程。这个函数的主要任务是从 TCP 接收队列中提取数据,并将这些数据拷贝到用户空间提供的缓冲区中。 以下是 tcp_recvmsg 函数的一般工作流程和功能解释: 函数签名和参数 int tcp_re…...
《数据结构》(应用题)
历年真题(09~24) 2009 最短路径(Dijkstra青春版) 【2009统考真题】带权图(权值非负,表示边连接的两顶点间的距离)的最短路径问题是找出从初始顶点到目标顶点之间的一条最短路径。假设从初始顶点…...
阿里内部正式开源“Spring Cloud Alibaba (全彩小册)”
年轻的毕业生们满怀希望与忐忑,去寻找、竞争一个工作机会。已经在职的开发同学,也想通过社会招聘或者内推的时机争取到更好的待遇、更大的平台。 然而,面试人群众多,技术市场却相对冷淡,面试的同学们不得不面临着 1 个…...
LeetCode题练习与总结:根据字符出现频率排序--451
一、题目描述 给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。 返回 已排序的字符串 。如果有多个答案,返回其中任何一个。 示例 1: 输入: s "tree" 输出: "eert" …...
Excel VBA学习系列汇总20241205
整理几年工作中,实用VBA代码,绝对干货! 方便自己查询,方便大家学习, 有缘人可复制使用,记得分享给大家免费学习哦! 序历史文章1新学期开始,如何新学期开始,如何按成绩名次…...
给el-table表头添加icon图标,以及鼠标移入icon时显示el-tooltip提示内容
在你的代码中,你已经正确地使用了 el-tooltip 组件来实现鼠标划过加号时显示提示信息。el-tooltip 组件的 content 属性设置了提示信息的内容,placement 属性设置了提示信息的位置。 你需要确保 el-tooltip 组件的 content 属性和 placement 属性设置正…...
基于LLM智能问答系统【阿里云:天池比赛】
流程: 1、分别识别问题及提供的资料文件中的公司名实体,有公司名的走语义检索,无公司名的走结构化召回 2、结构化召回:Qwen根据问题生成sql,执行sql获取结果数值,把结果数值与问题给到Qwen生成最终结果 …...
k8s-Informer概要解析(2)
Client-go 主要用在 k8s 控制器中 什么是 k8s Informer Informer 负责与 kubernetes APIServer 进行 Watch 操作,Watch 的资源,可以是 kubernetes 内置资源对象,也可以 CRD。 Informer 是一个带有本地缓存以及索引机制的核心工具包&#x…...
Leetcode 3376. Minimum Time to Break Locks I
Leetcode 3376. Minimum Time to Break Locks I 1. 解题思路2. 代码实现 题目链接:3376. Minimum Time to Break Locks I 1. 解题思路 这一题我最开始的思路走的是贪婪算法的路子,优先走X的增长,不过很不幸失败了,后面还是暴力…...
介绍8款开源网络安全产品
01 HFish蜜罐 HFish是一款开源的蜜罐系统,用于模拟各种网络服务和应用,以吸引潜在的黑客攻击。它能够记录攻击尝试并收集攻击者的信息,从而帮助网络管理员识别潜在的威胁。HFish支持多种协议和服务,包括HTTP、FTP、SSH等&#…...
vue2面试题|[2024-12-5]
开题答辩终于结束了,又要开始我的前端面试学习啦!!! 1.v-model双向绑定原理 class Vue{constructor(options){this.$options optionsthis.$watchEvent {}if(typeof options.beforeCreate function){options.beforeCreate.bind…...
共筑数字安全防线,2024开源和软件安全沙龙即将启幕
随着数字化转型进程的加快以及开源代码的广泛应用,开源凭借平等、开放、协作、共享的优秀创作模式,逐渐成为推动数字技术创新、加速传统行业转型升级的重要模式。但随着软件供应链日趋复杂多元,使得其安全风险不断加剧,针对软件供…...
目标跟踪领域经典论文解析
亲爱的小伙伴们😘,在求知的漫漫旅途中,若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界,亦或是读研论文的撰写攻略有所探寻🧐,那不妨给我一个小小的关注吧🥰。我会精心筹备,在…...
SQL DQL数据查询语言(后续)
SQL DQL数据查询语言(后续) 1.子查询 在查询语句中的WHERE条件子句中,又嵌套了另外一个查询语句在返回列中嵌套一个查询 where条件中嵌套 要求:查询课程为《高等数学-2》且分数不小于80分的学生的学号和姓名select a.StudentNo,a…...
Gitee配置SSH公钥
采用SSH协议同步Git仓库代码的好处就是高效。在配置好SSH公钥后,不需要每次操作都要输入用户名和密码(主要针对命令行来说)。 以我个人项目为例。 生成 SSH 公钥 1. 通过命令 ssh-keygen 生成 SSH Key: ssh-keygen -t ed25519…...
机器学习——感知机模型
文章目录 前言1.感知机模型介绍1.1基本概念1.2数学表达1.3几何解释1.4优缺点 2.二分类应用2.1应用介绍2.2准备数据集2.2.1环境检查2.2.2数据集介绍2.2.3获取数据2.2.4划分数据集 2.3可视化训练集2.4训练过程2.4.1首轮梯度下降2.4.2多轮梯度下降 2.5可视化分类结果2.6在验证集验…...
如何选择安全、可验证的技术?
澳大利亚信号局的澳大利亚网络安全中心 (ASD 的 ACSC) 发布了一份指导文件,题为《选择安全和可验证的技术》,旨在帮助组织在采购软件(专有或开源)、硬件(例如物联网设备)和云服务(SaaS、MSP 服务…...
STL库中list的使用与迭代器的实现
STL库中list的使用与迭代器的实现 1.使用list中的部分函数assignspliceremoveuniquemeger 2.list的部分功能实现(重点)框架迭代器的实现 1.使用list中的部分函数 assign 功能一:当前链表的节点全部销毁,替换成迭代区间的值 功能二…...
android 常用三方框架
说实话, 我是比较讨厌三方框架的, 比如一个eventbus 底层逻辑就是个观察者模式,当然他的场景涵盖的比较丰富, 单从 单一原则来说, 还是一个简单的观察者模式就能解决问题, 何必要添加那么多文件到我们的项目…...
Browser.js断点续传上传
通过断点续传上传的方式将文件上传到OSS前,您可以指定断点记录点。上传过程中,如果出现网络异常或程序崩溃导致文件上传失败时,将从断点记录处继续上传未上传完成的部分。 attention: 1、 当您使用webpack或browserify等打包工具…...
Java项目实战II基于微信小程序的无中介租房系统(开发文档+数据库+源码)
目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着城市化进程的加速,租房市场日益繁荣&a…...
了解Cocoa Touch框架与主要组件
Cocoa Touch框架详解及其主要组件 一、Cocoa Touch框架概述 Cocoa Touch框架是苹果公司为iOS应用程序开发提供的一套完整的框架,它基于Cocoa框架,并专为触控设备如iPhone、iPad等设计。这套框架不仅包含了构建图形用户界面(GUI)…...
ISO45001职业健康安全管理体系涵盖了丰富的内容
范围与术语 适用范围:明确规定了该标准适用于任何有愿望建立、实施和保持职业健康安全管理体系的组织,旨在使组织能够通过管理体系的有效运行,预防和控制职业健康安全风险,持续改进职业健康安全绩效。术语定义:对职业…...
Spring Boot 整合 Druid 并开启监控
文章目录 1. 引言2. 添加依赖3. 配置数据源4. 开启监控功能5. 自定义 Druid 配置(可选)6. 访问监控页面7. 注意事项8. 总结 Druid 是一个由阿里巴巴开源的高性能数据库连接池,它不仅提供了高效的连接管理功能,还自带了强大的监控和…...
【JAVA高级篇教学】第一篇:Springboot对接通义千问大模型
博主今天打算讲解下Java如何对接阿里云的通义千问大模型,可以自己玩玩ai问答之类的! 目录 一、发展历程 二、API-KEY的获取与配置 三、引用SDK 四、文本模型 1.代码 2.返回数据 3.官方代码案例 五、通义千问VL 1.计量计费 六、查看API-KEY调用额…...
【Windows 同时安装 MySQL5 和 MySQL8 - 详细图文教程】
卸载 MySQL 参考文章: 完美解决Mysql彻底删除并重装_怎么找到mysql并卸载-CSDN博客使用命令卸载mysql_卸载mysql服务命令-CSDN博客 先管理员方式打开 cmd ,切换到 MySQL 安装目录的 bin 文件夹下,执行如下命令,删除 MySQL 服务 my…...
Next.js 系统性教学:深入理解缓存与数据优化策略
更多有关Next.js教程,请查阅: 【目录】Next.js 独立开发系列教程-CSDN博客 目录 前言 1. 缓存的基本概念 1.1 缓存的作用 1.2 Next.js 中的缓存策略 2. Next.js 的缓存机制 2.1 请求记忆化(Request Memoization) 2.1.1 什…...
JAVA数据结构
1.数组 (Array): 固定大小的容器,用于存储相同类型的元素,数组在内存中是连续存储的,支持通过索引快 速访问元素。 int[] numbers = new int[10]; numbers[0] = 1;2.Java Collections Framework (JCF) JCF提供了一组接口和类用于管理和操作集合(如列表,集合,…...
力扣第96题 不同的二叉搜索树
力扣第96题 - 不同的二叉搜索树 题目描述 给定一个整数 n,求以 1 到 n 为节点组成的所有 不同的二叉搜索树(BST) 的个数。 题目分析 二叉搜索树的性质 对于一个二叉搜索树,以 i 为根节点: 左子树的节点值来自 [1, i…...
在Ubuntu上使用IntelliJ IDEA:开启你的Java开发之旅!
你好,年轻的学徒!🧑💻 是时候踏上进入Java开发世界的史诗之旅了,我们的得力助手将是强大的IntelliJ IDEA。准备好了吗?出发吧! 在我们开始之前,我们需要下载这个工具。但是&#…...
【C语言】18. 自定义类型:结构体类型
文章目录 前言:一、结构体类型的声明1、结构体回顾1)结构的声明2)结构体变量的创建和初始化 2、结构的特殊声明3、结构的⾃引⽤ 二、结构体变量的创建和初始化1、对⻬规则2、为什么存在内存对⻬?3、修改默认对⻬数 三、结构成员访问操作符1、…...
智能租赁管理系统助力规范化住房租赁市场提升用户体验
内容概要 在当今的住房租赁市场中,智能租赁管理系统应运而生,为房东和租客带来了前所未有的便利。这套系统就像一位全能助手,将租赁信息、监管机制以及在线签约功能集成在一起,让整个过程变得流畅而高效。换句话说,您…...
ERROR: KeeperErrorCode = NoNode for /hbase/master
原因分析 通过上面的情景模拟,我们可以看到报错的原因在于zookeeper中出现问题,可能是zookeeper中的/hbase/master被删除,或者是在hbase集群启动之后重新安装了zookeeper,导致zookeeper中的/hbase/master节点数据异常。 1. 停止…...
springboot第84集:Java进阶之路, Netty
# kafka-map文件夹 cd /usr/local/kafka-map # 根据需求自行修改配置 vi application.yml # 启动 java -jar kafka-map.jar byte minByte -128; byte maxByte 127; 用于表示一个 8 位(1 字节)有符号整数。它的值范围是 -128(-2^7࿰…...
DevOps持续集成
DevOps流程 第一步安装git 关闭防火墙 systemctl stop firewalld cd /usr/loacl vim docker-compose.yml docker search gitlab 拉取gitlab镜像 2.33GB docker pull gitlab/gitlab-ce:latestvim docker-compose.yml修改docker-compose.yml version: 3.1 services:gitlab:i…...
sql server log文件
确定 SQL Server 实例中具有大量 VDF 的数据库 SELECT [name], COUNT(l.database_id) AS vlf_count FROM sys.databases AS s CROSS APPLY sys.dm_db_log_info(s.database_id) AS l GROUP BY [name] HAVING COUNT(l.database_id) > 100; 在收缩日志文件之前确定事务日志中…...