配置访问本地ollama

18 阅读4分钟

配置访问本地ollama

本章我们将实现一个简单的对话窗口,窗口上部有一个下拉框,刷新可以显示我们本地的ollama提供的模型列表,下部有输入窗口,点击发送按钮可以将输入文本提交给我们本地的ollama模型处理。

安装ollama和下载模型

首先我们到Ollama官网下载安装ollama。下载完成后在命令行窗口执行如下命令下载ollama模型。cline官方推荐使用qwen3-coder作为本地模型。执行下边命令下载qwen3-coder:latest,该模型为30b参数,在我本机运行显示占用16G显存。

PS C:\Users\xuruo> ollama pull qwen3-coder:latest
...

为了在我们的下拉框界面中可以显示多个模型,我们可以再下载几个qwen3系列的小模型,比如qwen3:0.6b和qwen3-vl:2b(可以用来解析图片内容),下载完成后执行如下命令确认。

PS C:\Users\xuruo> ollama list
NAME                       ID              SIZE      MODIFIED
qwen3-coder:latest         06c1097efce0    18 GB     10 days ago
deepseek-ocr:3b            0e7b018b8a22    6.7 GB    3 weeks ago
qwen3-vl:2b                0635d9d857d4    1.9 GB    6 weeks ago
qwen3:0.6b                 7df6b6e09427    522 MB    6 weeks ago

让我们输入一个简单的对话确保ollama正常工作,让我们运行ollama run qwen3:0.6b开启对话,输入“用C语言写一个简单的hello world的例子”看模型如何应答。

PS C:\Users\xuruo> ollama run qwen3:0.6b
>>> 用C语言写一个简单的hello world的例子
Thinking...
好的,我现在需要帮用户用C语言写一个简单的hello world的例子。首先,我得确认用户的需求是什么。用户可能对C语言不太熟悉
,或者想练习基础语法,所以需要一个清晰、简洁的示例。
...
...done thinking.

以下是一个简单的C语言示例,输出“Hello World!”:

#include <stdio.h>

int main() {
    printf("Hello World!\n");
    return 0;
}

### 说明:
1. **包含头文件**`#include <stdio.h>` 是C语言的基础指令,用于访问标准输入输出功能。
2. **定义主函数**`int main()` 是C语言标准函数,用于程序的入口。
3. **输出内容**`printf("Hello World!\n");` 输出指定文本,并换行。

### 运行方式:
1. 将上述代码保存为 `.c` 文件,例如 `hello_world.c`2. 编译并运行程序:
   gcc -o hello_world hello_world.c
   ./hello_world

### 输出结果:

Hello World!

>>> Send a message (/? for help)

输入 /bye 退出对话

添加新的proto定义获取ollama模型列表

新的ollama.proto内容如下.

proto/minicline/ollama.proto

syntax = "proto3";

package minicline;

import "minicline/common.proto";

service OllamaService {
    rpc listModels(EmptyRequest) returns (StringArray);
}

我们参照cline的实现,将公用的EmptyRequestStringArray放入common.proto

proto/minicline/common.proto

syntax = "proto3";

package minicline;

message EmptyRequest {}

message StringArray {
  repeated string values = 1;
}

运行npm run proto重新生成代码,现在我们的webview-ui/src/services/grpc-client.ts中多了如下的代码用来获取ollama模型列表.

// GENERATED CODE -- DO NOT EDIT!
// Generated by scripts\generate-protobus-setup.mjs
import * as proto from "@shared/proto/index"
import { ProtoBusClient, Callbacks } from "./grpc-client-base"

export class OllamaServiceClient extends ProtoBusClient {
	static override serviceName: string = "minicline.OllamaService"
    static async listModels(request: proto.minicline.EmptyRequest): Promise<proto.minicline.StringArray> {
		return this.makeUnaryRequest("listModels", request, proto.minicline.EmptyRequest.toJSON, proto.minicline.StringArray.fromJSON)
	}
}

同上一章节一样,我们同样需要在extension host这端实现ollama.listModels,我们使用axios库访问本地ollama服务器API端口获取模型列表,具体代码如下.

src/core/controller/ollama/listModels.ts

import { EmptyRequest, StringArray } from "@/shared/proto/minicline/common";
import { Controller } from "..";
import axios from "axios";

export async function listModels(_controller: Controller, request: EmptyRequest): Promise<StringArray> {
	try {
		const baseUrl = "http://localhost:11434";

		if (!URL.canParse(baseUrl)) {
			return StringArray.create({ values: [] });
		}

		const response = await axios.get(`${baseUrl}/api/tags`);
		const modelsArray = response.data?.models?.map((model: any) => model.name) || [];
		const models = [...new Set<string>(modelsArray)].sort();

		return StringArray.create({ values: models });
	} catch (_error) {
		return StringArray.create({ values: [] });
	}
}

我们通过访问**http://localhost:11434/api/tags**获取到ollama的模型列表,我们需要将ollama作为服务起来,在命令行输入如下命令。

PS C:\Users\xuruo> ollama serve
Error: listen tcp 0.0.0.0:11434: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.

如果你遇到上边一样的信息,说明你的ollama守护进程已经启动(如果你照我前边的教程运行过诸如ollama list之类的命令),为了后边更好的查看ollama服务是否接收到我们的请求,我们先关掉守护进程,在windows上直接右键退出ollama托盘程序。

chapter-5-1.png

再次运行ollama serve,获取类似下边信息。

PS C:\Users\xuruo> ollama serve
time=2026-01-20T16:15:57.550+08:00 level=INFO source=routes.go:1554 msg="server config" env="map[CUDA_VISIBLE_DEVICES: GGML_VK_VISIBLE_DEVICES: GPU_DEVICE_ORDINAL: HIP_VISIBLE_DEVICES: HSA_OVERRIDE_GFX_VERSION: HTTPS_PROXY: HTTP_PROXY: NO_PROXY: OLLAMA_CONTEXT_LENGTH:4096 OLLAMA_DEBUG:INFO OLLAMA_FLASH_ATTENTION:false OLLAMA_GPU_OVERHEAD:0 OLLAMA_HOST:http://0.0.0.0:11434 OLLAMA_KEEP_ALIVE:5m0s 
...
OLLAMA_ORIGINS:[http://localhost https://localhost http://localhost:* https://localhost:* http://127.0.0.1 https://127.0.0.1 http://127.0.0.1:* https://127.0.0.1:* http://0.0.0.0 https://0.0.0.0 http://0.0.0.0:* https://0.0.0.0:* app://* file://* tauri://* vscode-webview://* vscode-file://*] OLLAMA_REMOTES:[ollama.com] OLLAMA_SCHED_SPREAD:false OLLAMA_VULKAN:false ROCR_VISIBLE_DEVICES:]"
time=2026-01-20T16:15:57.560+08:00 level=INFO source=images.go:493 msg="total blobs: 34"
...
time=2026-01-20T16:15:58.084+08:00 level=INFO source=routes.go:1648 msg="entering low vram mode" "total vram"="16.0 GiB" threshold="20.0 GiB"

修改webview为Chat UI

接下来我们修改我们的webview,将它改造成一个简单的对话界面。首先我们将主界面分为上中下三个部分,上边我们用来显示当前选中的模型,中间用来显示对话信息,下边是我们的对话输入框。我们将上边和下边分别提取出来作为单独react组件。

首先是我们的App.tsx,我们分了上中下三部分,上部和下部分别引用了ModelBar和Chatbox组件

webview-ui/src/App.tsx

import "./App.css";
import Chatbox from "./components/chat/chat";
import ModelBar from "./components/models/models";

function App() {
  return (
    <div className="container">
      <div className="top">
        <ModelBar/>
      </div>
      <div className="center"></div>
      <div className="bottom">
        <Chatbox/>
      </div>
    </div>
  );
}

export default App;

我们新建webview-ui/src/components目录,在下边新建models和chat子目录,分别用来存放ModelBar和Chatbox组件对应的tsx文件和css文件.

webview-ui/src/components/models/models.tsx

import { OllamaServiceClient } from "@/services/grpc-client";
import { EmptyRequest, StringArray } from "@shared/proto/minicline/common";
...
import { useCallback, useState } from "react";
...
function ModelBar() {
    const initModel: string[] = [];
    const [models, setModels] = useState(initModel);

    const handleListModelsMessage = useCallback(
        async () => {
            const result: StringArray = await OllamaServiceClient.listModels(EmptyRequest.create());
            console.log("models:", result)
            setModels(result.values);
        }, [models]
    );

    return (
        <>
            <div className="models-container">
                <span>Model:</span>
                <VSCodeDropdown id="models" className="modellist">
                    {models?.map((model: string) => (
                        <VSCodeOption key={model} value={model}>
                            {model}
                        </VSCodeOption>
                    ))}
                </VSCodeDropdown>
                <VSCodeButton id="listmodels-button" className="listmodelBtn" onClick={handleListModelsMessage}>Refresh</VSCodeButton>
            </div>
        </>
    );
}

export default ModelBar;

在ModelBar组件中我们使用react的useState来定义一个名为models的状态,下拉框内容为该状态值,点击Refresh按钮会调用OllamaServiceClient.listModels来更新该状态。

Chatbox组件内容比较简单,这章我们只定义界面,不做任何操作。

运行npm run build:webview重新编译webview,启动插件项目,点击Refresh按钮,会得到如下图所示界面(确保ollama serve启动正常)。

chapter-5-2.png

引入codicon

接下来我们将引入codicon,这是vscode内置的一套图标,我们将使用其中的图标替换我们的Refresh和Send按钮。

首先我们需要引入codcion库.

package.json

{
  ...
  "dependencies": {
    ...
    "@vscode/codicons": "^0.0.36",
    "axios": "^1.13.2"
  }
}

运行npm run install:all

然后我们需要在webview provider中引用该css并且设置新的Content-Security-Policy规则(因为该css会自动引用对应的font)。

src/providers/WeatherViewProvider.ts

...
export class WeatherViewProvider implements WebviewViewProvider {
  ...
  private _getWebviewContent(webview: Webview, extensionUri: Uri) {
    ...
    const codiconsUrl = getUri(webview, extensionUri, ["node_modules", "@vscode", "codicons", "dist", "codicon.css"]);
    ...
    return /*html*/ `
      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <meta http-equiv="Content-Security-Policy" content="default-src 'none';
          font-src ${webview.cspSource} data:;  
          img-src ${webview.cspSource} https: data:; 
          style-src ${webview.cspSource}; 
          script-src 'nonce-${nonce}';">
          <link rel="stylesheet" type="text/css" href="${stylesUri}">
          <link href="${codiconsUrl}" rel="stylesheet" />
          <title>Hello World</title>
        </head>
        <body>
          <div id="root"></div>
          <script type="module" nonce="${nonce}" src="${scriptUri}"></script>
        </body>
      </html>
    `;
  }
}

最后我们修改ModelBar和Chatbox组件,将其中的VSCodeButton替换成对应的图标。

webview-ui/src/components/models/models.tsx

...
function ModelBar() {
    ...
    return (
        <>
            <div className="models-container">
                <span>Model:</span>
                <VSCodeDropdown id="models" className="modellist">
                    {models?.map((model: string) => (
                        <VSCodeOption key={model} value={model}>
                            {model}
                        </VSCodeOption>
                    ))}
                </VSCodeDropdown>
                <span className="codicon codicon-refresh" onClick={handleListModelsMessage}></span>
            </div>
        </>
    );
}
...

webview-ui/src/components/chat/chat.tsx

...
function Chatbox() {
    return (
        <>
            <div className="chat-box">
                <div>
                    <VSCodeTextArea className="chatinput"
                        placeholder="say hello"
                        value="Create a hello world application in C.">
                    </VSCodeTextArea>
                </div>
                <div className="button-container">
                <div>
                    <span className="codicon codicon-send"></span>
                </div>
                </div>
            </div>
        </>
    );
}
...

运行npm run build:webview重新编译react界面,重启插件项目,点击刷新图标,如果一切正常会看到如下界面。

chapter-5-3.png

总结

本章我们学习了利用之前的proto框架添加新的服务,并且修改了react界面访问本地ollama服务,也在我们的react界面中使用了vscode的图标。下一章我们将访问ollama对话服务,将模型回答的内容写入文件。