├── render.yaml ├── deno.json ├── .github └── workflows │ ├── deno.yml │ ├── commitlint.yml │ └── docker.yml ├── src ├── cron.ts ├── auth.ts ├── limit.ts ├── api │ ├── models.ts │ └── chat.ts └── cache.ts ├── Dockerfile ├── main.ts ├── LICENSE ├── README_CN.md ├── README.md ├── deno.lock ├── cliff.toml └── CHANGELOG.md /render.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | - name: duckduckgo-ai-chat-service 3 | type: web 4 | runtime: image 5 | plan: free 6 | image: 7 | url: 8 | mumulhl/duckduckgo-ai-chat-service:latest 9 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "start": "deno run --unstable --allow-net --allow-env main.ts" 4 | }, 5 | "compilerOptions": { 6 | "jsx": "precompile", 7 | "jsxImportSource": "hono/jsx" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/deno.yml: -------------------------------------------------------------------------------- 1 | name: Deno Check 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: denoland/setup-deno@v2 11 | - run: deno fmt --check **/*.ts 12 | - run: deno lint 13 | 14 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request, push] 3 | 4 | permissions: 5 | contents: read 6 | pull-requests: read 7 | 8 | jobs: 9 | commitlint: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: wagoid/commitlint-github-action@v6 14 | -------------------------------------------------------------------------------- /src/cron.ts: -------------------------------------------------------------------------------- 1 | import * as cache from "./cache.ts"; 2 | 3 | function cron() { 4 | const cron_var = Deno.env.get("CLEAN_CACHE_CRON"); 5 | let cron = 1; 6 | if (cron_var !== undefined) { 7 | cron = Number(cron_var); 8 | } 9 | Deno.cron("Clean cache", { hour: { every: cron } }, () => { 10 | cache.chatCache.clear(); 11 | }); 12 | } 13 | 14 | export { cron }; 15 | -------------------------------------------------------------------------------- /src/auth.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "jsr:@hono/hono"; 2 | import { bearerAuth } from "jsr:@hono/hono/bearer-auth"; 3 | import { BlankEnv, BlankSchema } from "jsr:@hono/hono/types"; 4 | 5 | function auth(app: Hono) { 6 | const token = Deno.env.get("TOKEN"); 7 | if (token) { 8 | app.use("/v1/*", bearerAuth({ token })); 9 | } 10 | } 11 | 12 | export { auth }; 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM denoland/deno:2.2.1 2 | 3 | # The port that your application listens to. 4 | EXPOSE 8000 5 | 6 | WORKDIR /app 7 | 8 | # These steps will be re-run upon each file change in your working directory: 9 | COPY . . 10 | # Compile the main app so that it doesn't need to be compiled each startup/entry. 11 | RUN deno cache main.ts 12 | 13 | CMD ["run", "--allow-net", "--allow-env", "--unstable-cron", "main.ts"] 14 | -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "jsr:@hono/hono"; 2 | 3 | import { chat } from "./src/api/chat.ts"; 4 | import { models } from "./src/api/models.ts"; 5 | 6 | import { auth } from "./src/auth.ts"; 7 | import { limit } from "./src/limit.ts"; 8 | import { cron } from "./src/cron.ts"; 9 | 10 | const app = new Hono(); 11 | 12 | auth(app); 13 | limit(app); 14 | 15 | chat(app); 16 | models(app); 17 | 18 | cron(); 19 | 20 | Deno.serve(app.fetch); 21 | -------------------------------------------------------------------------------- /src/limit.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "jsr:@hono/hono"; 2 | import { BlankEnv, BlankSchema } from "jsr:@hono/hono/types"; 3 | 4 | import { rateLimiter } from "npm:hono-rate-limiter"; 5 | 6 | function limit(app: Hono) { 7 | const limit_var = Deno.env.get("LIMIT"); 8 | let limit = 2; 9 | if (limit_var !== undefined) { 10 | limit = Number(limit_var); 11 | } 12 | const limiter = rateLimiter({ 13 | windowMs: 1000, 14 | limit, 15 | standardHeaders: "draft-6", 16 | keyGenerator: (_) => "1", 17 | }); 18 | 19 | app.use("/v1/*", limiter); 20 | } 21 | 22 | export { limit }; 23 | -------------------------------------------------------------------------------- /src/api/models.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from "jsr:@hono/hono"; 2 | import { BlankEnv, BlankSchema } from "jsr:@hono/hono/types"; 3 | 4 | function models(app: Hono) { 5 | app.get("/v1/models", (c) => { 6 | return c.json({ 7 | data: [{ 8 | "id": "gpt-4o-mini", 9 | "object": "model", 10 | "owned_by": "OpenAI", 11 | }, { 12 | "id": "claude-3-haiku", 13 | "object": "model", 14 | "owned_by": "Anthropic", 15 | }, { 16 | "id": "llama", 17 | "object": "model", 18 | "owned_by": "Meta", 19 | }, { 20 | "id": "mixtral", 21 | "object": "model", 22 | "owned_by": "Mixtral", 23 | }, { 24 | "id": "o3-mini", 25 | "object": "model", 26 | "owned_by": "OpenAI", 27 | }], 28 | }); 29 | }); 30 | } 31 | 32 | export { models }; 33 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build Docker Image 2 | on: 3 | push: 4 | tags: 5 | - 'v[0-9]+.[0-9]+.[0-9]+' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Set up QEMU 16 | uses: docker/setup-qemu-action@v3 17 | 18 | - name: Set up Docker Buildx 19 | id: buildx 20 | uses: docker/setup-buildx-action@v3 21 | 22 | - name: Login to DockerHub 23 | uses: docker/login-action@v3 24 | with: 25 | username: ${{ secrets.DOCKERHUB_USERNAME }} 26 | password: ${{ secrets.DOCKERHUB_TOKEN }} 27 | 28 | - name: Build and push 29 | uses: docker/build-push-action@v6 30 | with: 31 | platforms: linux/amd64,linux/arm64/v8 32 | push: true 33 | tags: mumulhl/duckduckgo-ai-chat-service:latest 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Mumulhl 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /src/cache.ts: -------------------------------------------------------------------------------- 1 | import { Chat } from "jsr:@mumulhl/duckduckgo-ai-chat@3"; 2 | 3 | type Messages = { content: string; role: "user" | "assistant" | "system" }[]; 4 | 5 | const chatCache = new Map(); 6 | 7 | function setCache(chat: Chat) { 8 | const messages_only_content = chat.messages.map((m) => m.content); 9 | const stringify = JSON.stringify(messages_only_content); 10 | chatCache.set(stringify, chat); 11 | } 12 | 13 | function setRedoCache(chat: Chat) { 14 | const chatRedo = structuredClone(chat); 15 | chatRedo.messages.pop(); 16 | chatRedo.messages.pop(); 17 | chatRedo.newVqd = chatRedo.oldVqd; 18 | setCache(chatRedo); 19 | } 20 | 21 | function findCache(messages: Messages): Chat | undefined { 22 | const messages_only_content = messages.map((m) => m.content); 23 | const stringifyRedo = JSON.stringify(messages_only_content); 24 | let chat = chatCache.get(stringifyRedo); 25 | if (chat) { 26 | // redo 27 | return chat; 28 | } else { 29 | messages_only_content.pop(); 30 | const stringify = JSON.stringify(messages_only_content); 31 | chat = chatCache.get(stringify); 32 | removeCache(messages_only_content); 33 | 34 | messages_only_content.pop(); 35 | messages_only_content.pop(); 36 | removeCache(messages_only_content); 37 | 38 | return chat; 39 | } 40 | } 41 | 42 | function removeCache(messages_only_content: string[]) { 43 | const stringify = JSON.stringify(messages_only_content); 44 | chatCache.delete(stringify); 45 | } 46 | 47 | export { chatCache, findCache, setCache, setRedoCache }; 48 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Duckduckgo AI Chat Service 2 | 3 | ![Docker Pulls](https://img.shields.io/docker/pulls/mumulhl/duckduckgo-ai-chat-service) 4 | ![GitHub Tag](https://img.shields.io/github/v/tag/mumu-lhl/duckduckgo-ai-chat-service) 5 | 6 | [English](./README.md) | 中文 7 | 8 | 为 [Duckduckgo AI Chat](https://duckduckgo.com/aichat) 提供兼容 OpenAI 的 API,可以免费使用 o3-mini, gpt-4o-mini、claude-3-haiku、llama3.3... 9 | 10 | ## 部署 11 | 12 | ### Render 13 | 14 | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/mumu-lhl/duckduckgo-ai-chat-service) 15 | 16 | ### Docker 17 | 18 | ```sh 19 | docker run -d mumulhl/duckduckgo-ai-chat-service 20 | ``` 21 | 22 | ### Deno Deploy 23 | 24 | #### Web 25 | 26 | fork 本项目,访问 ,登录后点击创建新项目。 27 | 28 | #### 命令行 29 | 30 | ```sh 31 | git clone https://github.com/mumu-lhl/duckduckgo-ai-chat-service --depth 1 32 | deno install -A jsr:@deno/deployctl 33 | deployctl deploy 34 | ``` 35 | 36 | ### Deno 37 | 38 | ```sh 39 | # 直接运行 40 | deno run --allow-env --allow-net --unstable-cron https://raw.githubusercontent.com/mumu-lhl/duckduckgo-ai-chat-service/main/main.ts 41 | 42 | # 安装后运行 43 | deno install -g --allow-env --allow-net --unstable-cron -n duckduckgo-ai-chat-service https://raw.githubusercontent.com/mumu-lhl/duckduckgo-ai-chat-service/main/main.ts 44 | duckduckgo-ai-chat-service 45 | ``` 46 | 47 | ## 配置 48 | 49 | 采用环境变量进行配置: 50 | 51 | * TOKEN - 限制可以访问 API 的 token,如果不填,任意的 token 都可以访问 API 52 | * LIMIT - 每秒请求速率限制,默认为 2 53 | * CLEAN_CACHE_CRON - 每个多少小时清理缓存,默认为 1 54 | 55 | ## 使用 56 | 57 | 将需要用到 OpenAI API 的地方的 base url 改成部署得到的即可。 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Duckduckgo AI Chat Service 2 | 3 | ![Docker Pulls](https://img.shields.io/docker/pulls/mumulhl/duckduckgo-ai-chat-service) 4 | ![GitHub Tag](https://img.shields.io/github/v/tag/mumu-lhl/duckduckgo-ai-chat-service) 5 | 6 | English | [中文](./README_CN.md) 7 | 8 | Provide an OpenAI-compatible API for [Duckduckgo AI Chat](https://duckduckgo.com/aichat) that can be used for free with o3-mini, gpt-4o-mini, claude-3-haiku, llama3.3... 9 | 10 | ## Notice 11 | 12 | **Duckduckgo has made cracking much harder, and I am unable to maintain this project.** 13 | 14 | ## Deploy 15 | 16 | ### Render 17 | 18 | [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/mumu-lhl/duckduckgo-ai-chat-service) 19 | 20 | ### Docker 21 | 22 | ```sh 23 | docker run -p 8000:8000 -d mumulhl/duckduckgo-ai-chat-service 24 | ``` 25 | 26 | ### Deno Deploy 27 | 28 | #### Web 29 | 30 | Fork this project, then visit and create new project after loging in. 31 | 32 | #### Command line 33 | 34 | ```sh 35 | git clone https://github.com/mumu-lhl/duckduckgo-ai-chat-service --depth 1 36 | deno install -A jsr:@deno/deployctl 37 | deployctl deploy 38 | ``` 39 | 40 | ### Deno 41 | 42 | ```sh 43 | # Run directly 44 | deno run --allow-env --allow-net --unstable-cron https://raw.githubusercontent.com/mumu-lhl/duckduckgo-ai-chat-service/main/main.ts 45 | 46 | # Run after installation 47 | deno install -g --allow-env --allow-net --unstable-cron -n duckduckgo-ai-chat-service https://raw.githubusercontent.com/mumu-lhl/duckduckgo-ai-chat-service/main/main.ts 48 | duckduckgo-ai-chat-service 49 | ``` 50 | 51 | ## Configuration 52 | 53 | Configuration using environment variables: 54 | 55 | * TOKEN - Limit the tokens that can access the API, if you don't fill in, any token can access the API. 56 | * LIMIT - limit the request rate per second, default is 2 57 | * CLEAN_CACHE_CRON - how many hours to clean up the cache, default is 1 58 | 59 | ## Usage 60 | 61 | Just change the base url of the place where you need to use the OpenAI API to the one you deployed. 62 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4", 3 | "specifiers": { 4 | "jsr:@hono/hono@*": "4.7.0", 5 | "jsr:@lukeed/fetch-event-stream@*": "0.1.5", 6 | "jsr:@lukeed/fetch-event-stream@~0.1.5": "0.1.5", 7 | "jsr:@mumulhl/duckduckgo-ai-chat@3": "3.3.0", 8 | "jsr:@mumulhl/duckduckgo-ai-chat@3.2.0": "3.2.0", 9 | "jsr:@mumulhl/duckduckgo-ai-chat@3.3.0": "3.3.0", 10 | "jsr:@std/http@0.221": "0.221.0", 11 | "jsr:@std/streams@0.221": "0.221.0", 12 | "npm:hono-rate-limiter@*": "0.4.2_hono@4.7.0" 13 | }, 14 | "jsr": { 15 | "@hono/hono@4.7.0": { 16 | "integrity": "1eb10e1976d3dda22ab43115ecfa348a43cd17c8aea5c0039c9757ebded624f8" 17 | }, 18 | "@hono/hono@4.7.2": { 19 | "integrity": "73466a24bd6eb3b527cde18e59ca5543890ddacb4312fc0bc7504d28a7e57a38" 20 | }, 21 | "@lukeed/fetch-event-stream@0.1.5": { 22 | "integrity": "50fe145b95e6ead49584c4f00dbb266c34a572e2ac97a924463f567887cd99ce", 23 | "dependencies": [ 24 | "jsr:@std/http", 25 | "jsr:@std/streams" 26 | ] 27 | }, 28 | "@mumulhl/duckduckgo-ai-chat@3.2.0": { 29 | "integrity": "c8dcbc98506f8a86f45886acba7a576a0ebbd9122e9af83485edfe43058527b6", 30 | "dependencies": [ 31 | "jsr:@lukeed/fetch-event-stream@~0.1.5" 32 | ] 33 | }, 34 | "@mumulhl/duckduckgo-ai-chat@3.3.0": { 35 | "integrity": "4455acb54c8513ba5defccd3da57fefba61cc9a8b4ec5955a1a360a62f161884", 36 | "dependencies": [ 37 | "jsr:@lukeed/fetch-event-stream@~0.1.5" 38 | ] 39 | }, 40 | "@std/http@0.221.0": { 41 | "integrity": "35fdcfed1501f2052338bf7ffba5d66f1455f2fffc01e9d7cf13eed548fffbaa" 42 | }, 43 | "@std/streams@0.221.0": { 44 | "integrity": "47f2f74634b47449277c0ee79fe878da4424b66bd8975c032e3afdca88986e61" 45 | } 46 | }, 47 | "npm": { 48 | "hono-rate-limiter@0.4.2_hono@4.7.0": { 49 | "integrity": "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw==", 50 | "dependencies": [ 51 | "hono" 52 | ] 53 | }, 54 | "hono@4.7.0": { 55 | "integrity": "sha512-hV97aIR4WYbG30k234sD9B3VNr1ZWdQRmrVF76LKFlmI7O9Yo70mG9+mFwyQ6Sjrz4wH71GfnBxv6CPjcx3QNw==" 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # git-cliff ~ configuration file 2 | # https://git-cliff.org/docs/configuration 3 | 4 | [changelog] 5 | # template for the changelog footer 6 | header = """ 7 | # Changelog\n 8 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines.\n 9 | """ 10 | # template for the changelog body 11 | # https://keats.github.io/tera/docs/#introduction 12 | body = """ 13 | --- 14 | {% if version %}\ 15 | {% if previous.version %}\ 16 | ## [{{ version | trim_start_matches(pat="v") }}]($REPO/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} 17 | {% else %}\ 18 | ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} 19 | {% endif %}\ 20 | {% else %}\ 21 | ## [unreleased] 22 | {% endif %}\ 23 | {% for group, commits in commits | group_by(attribute="group") %} 24 | ### {{ group | striptags | trim | upper_first }} 25 | {% for commit in commits 26 | | filter(attribute="scope") 27 | | sort(attribute="scope") %} 28 | - **({{commit.scope}})**{% if commit.breaking %} [**breaking**]{% endif %} \ 29 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 30 | {%- endfor -%} 31 | {% raw %}\n{% endraw %}\ 32 | {%- for commit in commits %} 33 | {%- if commit.scope -%} 34 | {% else -%} 35 | - {% if commit.breaking %} [**breaking**]{% endif %}\ 36 | {{ commit.message }} - ([{{ commit.id | truncate(length=7, end="") }}]($REPO/commit/{{ commit.id }})) - {{ commit.author.name }} 37 | {% endif -%} 38 | {% endfor -%} 39 | {% endfor %}\n 40 | """ 41 | # template for the changelog footer 42 | footer = """ 43 | 44 | """ 45 | # remove the leading and trailing whitespace from the templates 46 | trim = true 47 | # postprocessors 48 | postprocessors = [ 49 | { pattern = '\$REPO', replace = "https://github.com/mumu-lhl/duckduckgo-ai-chat-service" }, # replace repository URL 50 | ] 51 | 52 | [git] 53 | # parse the commits based on https://www.conventionalcommits.org 54 | conventional_commits = true 55 | # filter out the commits that are not conventional 56 | filter_unconventional = true 57 | # process each line of a commit as an individual commit 58 | split_commits = false 59 | # regex for preprocessing the commit messages 60 | commit_preprocessors = [ 61 | { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/issues/${2}))"}, # replace issue numbers 62 | ] 63 | # regex for parsing and grouping commits 64 | commit_parsers = [ 65 | { message = "^feat", group = "Features" }, 66 | { message = "^fix", group = "Bug Fixes" }, 67 | { message = "^doc", group = "Documentation" }, 68 | { message = "^perf", group = "Performance" }, 69 | { message = "^refactor", group = "Refactoring" }, 70 | { message = "^style", group = "Style" }, 71 | { message = "^revert", group = "Revert" }, 72 | { message = "^test", group = "Tests" }, 73 | { message = "^chore\\(version\\):", skip = true }, 74 | { message = "^chore", group = "Miscellaneous Chores" }, 75 | { body = ".*security", group = "Security" }, 76 | ] 77 | # protect breaking changes from being skipped due to matching a skipping commit_parser 78 | protect_breaking_commits = false 79 | # filter out the commits that are not matched by commit parsers 80 | filter_commits = false 81 | # regex for matching git tags 82 | tag_pattern = "v[0-9].*" 83 | # regex for skipping tags 84 | skip_tags = "v0.1.0-beta.1" 85 | # regex for ignoring tags 86 | ignore_tags = "" 87 | # sort the tags topologically 88 | topo_order = false 89 | # sort the commits inside sections by oldest/newest order 90 | sort_commits = "oldest" 91 | # limit the number of commits included in the changelog. 92 | # limit_commits = 42 93 | -------------------------------------------------------------------------------- /src/api/chat.ts: -------------------------------------------------------------------------------- 1 | import { Chat, initChat, ModelAlias } from "jsr:@mumulhl/duckduckgo-ai-chat@3.3.0"; 2 | import { events } from "jsr:@lukeed/fetch-event-stream"; 3 | 4 | import { Hono } from "jsr:@hono/hono"; 5 | import { BlankEnv, BlankSchema } from "jsr:@hono/hono/types"; 6 | import { SSEStreamingApi, streamSSE } from "jsr:@hono/hono/streaming"; 7 | 8 | import * as cache from "./../cache.ts"; 9 | 10 | type Messages = { content: string; role: "user" | "assistant" | "system" | "developer" }[]; 11 | 12 | type MessageData = { 13 | id: string; 14 | model: string; 15 | created: number; 16 | role?: "assistant"; 17 | message?: string; 18 | }; 19 | 20 | class DataStream { 21 | id: string; 22 | model: string; 23 | created: number; 24 | choices: { 25 | delta: { content?: string; role?: "assistant" }; 26 | }[]; 27 | 28 | constructor(messageData: MessageData) { 29 | this.id = messageData.id; 30 | this.model = messageData.model; 31 | this.created = messageData.created; 32 | this.choices = [{ delta: { content: messageData.message } }]; 33 | if (messageData.role === "assistant") { 34 | this.choices[0].delta.role = "assistant"; 35 | } 36 | } 37 | } 38 | 39 | async function fetchFull(chat: Chat, messages: Messages) { 40 | let message: Response | undefined; 41 | let text: string = ""; // Initialize text 42 | let messageData: MessageData | undefined; 43 | 44 | for (let i = 0; i < messages.length; i += 2) { 45 | text = ""; // Reset the text at each loop to avoid stacking 46 | const content = messages[i].content; 47 | message = await chat.fetch(content); 48 | 49 | const stream = events(message as Response); 50 | for await (const event of stream) { 51 | if (!event.data || event.data === "[DONE]") { 52 | break; // End the loop if there's no data or received end message 53 | } 54 | 55 | try { 56 | messageData = JSON.parse(event.data) as MessageData; 57 | if (messageData.message === undefined) { 58 | break; 59 | } else { 60 | text += messageData.message; // Append message content 61 | } 62 | } catch (e) { 63 | console.error("Failed to parse JSON:", e); 64 | break; // End the loop on parse error 65 | } 66 | } 67 | 68 | const newVqd = message.headers.get("x-vqd-4") as string; 69 | chat.oldVqd = chat.newVqd; 70 | chat.newVqd = newVqd; 71 | 72 | chat.messages.push({ content: text, role: "assistant" }); 73 | } 74 | 75 | const { id, created, model } = messageData as MessageData; 76 | 77 | return { id, created, model, text }; 78 | } 79 | 80 | function fetchStream(chat: Chat, messages: Messages) { 81 | return async (s: SSEStreamingApi) => { 82 | for (let i = 0; i < messages.length; i += 2) { 83 | let text = ""; // Reset the text at each loop to avoid stacking 84 | let messageData: MessageData | undefined; 85 | 86 | const content = messages[i].content; 87 | const message = await chat.fetch(content); 88 | 89 | const stream = events(message as Response); 90 | 91 | for await (const event of stream) { 92 | if (!event.data || event.data === "[DONE]") { 93 | break; // End the loop if there's no data or received end message 94 | } 95 | 96 | try { 97 | messageData = JSON.parse(event.data); 98 | if (messageData?.message === undefined) { 99 | break; 100 | } else { 101 | text += messageData.message; // Append message content 102 | } 103 | } catch (e) { 104 | console.error("Failed to parse JSON:", e); 105 | break; // End the loop on parse error 106 | } 107 | 108 | if (i === messages.length - 1) { 109 | const dataStream = new DataStream(messageData); 110 | await s.writeSSE({ 111 | data: JSON.stringify(dataStream), 112 | }); 113 | } 114 | } 115 | 116 | const newVqd = message.headers.get("x-vqd-4") as string; 117 | chat.oldVqd = chat.newVqd; 118 | chat.newVqd = newVqd; 119 | 120 | chat.messages.push({ content: text, role: "assistant" }); 121 | } 122 | 123 | if (chat.messages.length >= 4) { 124 | cache.setCache(chat); 125 | cache.setRedoCache(chat); 126 | } 127 | }; 128 | } 129 | 130 | function chat(app: Hono) { 131 | app.post("/v1/chat/completions", async (c) => { 132 | const body = await c.req.json(); 133 | const stream: boolean = body.stream; 134 | const model_name: ModelAlias = body.model; 135 | let messages: Messages = body.messages; 136 | 137 | if (messages[0].role === "system" || messages[0].role === "developer") { 138 | messages[1].content = messages[0].content + messages[1].content; 139 | messages = messages.slice(1); 140 | } 141 | 142 | let chat = cache.findCache(messages); 143 | if (chat === undefined) { 144 | chat = await initChat(model_name); 145 | } else { 146 | messages = messages.slice(-1); 147 | } 148 | 149 | if (stream) { 150 | return streamSSE(c, fetchStream(chat, messages)); 151 | } 152 | 153 | const { id, model, created, text } = await fetchFull(chat, messages); 154 | 155 | if (chat.messages.length >= 4) { 156 | cache.setCache(chat); 157 | cache.setRedoCache(chat); 158 | } 159 | 160 | return c.json({ 161 | id, 162 | model, 163 | created, 164 | choices: [{ 165 | message: { role: "assistant", content: text }, 166 | }], 167 | }); 168 | }); 169 | } 170 | 171 | export { chat }; 172 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [conventional commits](https://www.conventionalcommits.org/) for commit guidelines. 4 | 5 | --- 6 | ## [1.4.0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.3.0..v1.4.0) - 2025-02-22 7 | 8 | ### Documentation 9 | 10 | - update README_CN.md - ([694f558](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/694f558afbab2b58b318d7dca3e31c6a7f5db325)) - Mumulhl 11 | 12 | ### Miscellaneous Chores 13 | 14 | - **(deps)** update duckduckgo-ai-chat to v3.3.0 - ([fec0583](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/fec05834a97b00a7ba4353360d680bc3c515ef65)) - Mumulhl 15 | - **(docker)** update Deno version to 2.2.1 - ([7e4c28a](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/7e4c28ad4b0eb404c48c8d05d1c760671d6c4a13)) - Mumulhl 16 | 17 | ### Dcos 18 | 19 | - update README.md - ([b9cd80f](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/b9cd80fa0e9aa014a307805fb7ef5030b9270f53)) - Mumulhl 20 | 21 | --- 22 | ## [1.3.0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.2.0..v1.3.0) - 2025-02-08 23 | 24 | ### Miscellaneous Chores 25 | 26 | - **(deps)** update duckduckgo-ai-chat to v3.2.0 - ([786f779](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/786f7796db7567cc732164f2dec8dae042df42c8)) - Mumulhl 27 | - **(deps)** update @hono/hono to v4.7.0 and hono-rate-limiter to v0.4.2 - ([f131239](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f131239f183d0121619a919a35febe6f59f527f5)) - Mumulhl 28 | 29 | ### Refactoring 30 | 31 | - update model ownership information - ([71b5a6e](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/71b5a6e0be34072bd3258a71a495ec79353ad33f)) - Mumulhl 32 | 33 | --- 34 | ## [1.2.0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.1.0..v1.2.0) - 2025-02-06 35 | 36 | ### Features 37 | 38 | - add o3-mini - ([aff954b](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/aff954bef75f1d1732a09620accd677488f59829)) - Mumulhl 39 | 40 | ### Miscellaneous Chores 41 | 42 | - add deno check workflow - ([610db63](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/610db632cb8ed4d0d7ea4e6041df69b85d7a16cc)) - Mumulhl 43 | - fix deno check workflow - ([8337172](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/8337172718281f180bc22297903d7a50cfa82a88)) - Mumulhl 44 | - add limit to deno workflow - ([76d2120](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/76d2120d467062bb662da2f04b92142adc5b57cb)) - Mumulhl 45 | - update denoland/setup-deno action to v2 - ([0dee56e](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/0dee56e4f857d21eba9214dd0f38ae3580393bd9)) - Mumulhl 46 | 47 | --- 48 | ## [1.1.0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.0.2..v1.1.0) - 2024-08-12 49 | 50 | ### Bug Fixes 51 | 52 | - change --unstable flag to --unstable-cron - ([f8a7d45](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f8a7d45674b0c9cbec13d5a2faff3b48f0d77c64)) - Mumulhl 53 | 54 | ### Documentation 55 | 56 | - add render deploy - ([d998118](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/d99811826e106da20e12269de91d0f381cc53b0c)) - Mumulhl 57 | - update readme - ([f66c423](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f66c42382bfb84c12c8063fa1014ad51c63eefef)) - Mumulhl 58 | - add badges - ([3112446](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/31124462d39764832ba23636056e10ba1880b995)) - Mumulhl 59 | 60 | ### Features 61 | 62 | - add /v1/models - ([ac921d5](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/ac921d51b27bb0035faa02ee12fd066e4f51c24e)) - Mumulhl 63 | 64 | ### Miscellaneous Chores 65 | 66 | - support render deploy - ([a76eb35](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/a76eb35d58d6ae97d7a2dd8875b38848456baab6)) - Mumulhl 67 | - update lockfile - ([f4c345d](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f4c345d908407563358ccc5da81c1570c54a59bb)) - Mumulhl 68 | - release v1.1.0 - ([0bb8f4c](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/0bb8f4c840c4cddcc4f169c86c0bd46ab8498900)) - Mumulhl 69 | 70 | ### Refactoring 71 | 72 | - optimize code structure - ([c36734e](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/c36734ec41cc19cb37e4e4e3e84e92c3211b86ce)) - Mumulhl 73 | 74 | --- 75 | ## [1.0.2](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.0.1..v1.0.2) - 2024-08-10 76 | 77 | ### Documentation 78 | 79 | - add Docker build to readme - ([9a99ae8](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/9a99ae878b7051e5c31d4fdfceaccfd75adb3828)) - Mumulhl 80 | 81 | ### Miscellaneous Chores 82 | 83 | - support docker build - ([83e5036](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/83e5036a37eb27451771641f8df33db9fc45182d)) - Mumulhl 84 | - add docker workflow - ([886ce38](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/886ce38e87e5c936634d85f99e7991836d148e53)) - Mumulhl 85 | - change port in Dockerfile - ([eb55b55](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/eb55b5517775649d194e020df5a090fa61688a1a)) - Mumulhl 86 | - release v1.0.2 - ([e4b115d](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/e4b115dd79f92bc9ad1e50902bdd8c7ce22c5af8)) - Mumulhl 87 | 88 | --- 89 | ## [1.0.1](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/compare/v1.0.0..v1.0.1) - 2024-08-09 90 | 91 | ### Bug Fixes 92 | 93 | - remove import specifier from code - ([a8e1837](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/a8e18379e5f290683fbce626fc6242b34a8298ff)) - Mumulhl 94 | - try to fix import path no prefixed with ... - ([7abcde2](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/7abcde22f77c74bcd21ae9f84bababe9886953f4)) - Mumulhl 95 | 96 | ### Documentation 97 | 98 | - add usage - ([07cbd0b](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/07cbd0b0a59e0df188f192c8df672540efd0d62d)) - Mumulhl 99 | 100 | ### Miscellaneous Chores 101 | 102 | - update duckduckgo-ai-chat to 3.0.0 - ([a7730cd](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/a7730cdeb4ef8963ee67335832032f0438a1ba66)) - Mumulhl 103 | - release v1.0.1 - ([5823950](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/5823950da426e2e34d626fe58bc1e8bc265f505d)) - Mumulhl 104 | 105 | ### Refactoring 106 | 107 | - error handling and connection management ([#1](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/issues/1)) - ([4b59309](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/4b59309daeda6994a794e44ef6447be03e0ccc1a)) - Le Minh Quan 108 | 109 | ### Revert 110 | 111 | - fix: try to fix import path no prefixed with ... - ([84178ab](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/84178ab178c9c7979dd96c9580a4db2fe875bcc3)) - Mumulhl 112 | - style: remove scope - ([46e2a9d](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/46e2a9dc1353cfaf1c9098757204f5ed5b2d207c)) - Mumulhl 113 | - fix: remove import specifier from code - ([bb36564](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/bb365645b0cb4958e54b0b3386eca33c54a8a4a4)) - Mumulhl 114 | 115 | ### Style 116 | 117 | - remove scope - ([f2abf0a](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f2abf0a0e40d7cfec7687d88791d36f98777e25f)) - Mumulhl 118 | 119 | --- 120 | ## [1.0.0] - 2024-08-03 121 | 122 | ### Bug Fixes 123 | 124 | - relative import path - ([2aae6f7](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/2aae6f7c6ec4b8f422634a5a6c827821dab96ca9)) - Mumulhl 125 | - relative import path - ([cea3a07](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/cea3a076f819b60c1e9113f5d57801d8f0f47ed7)) - Mumulhl 126 | - add prefix - ([f4268b7](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f4268b7471c55b9450382cb58d7f765c10f4d551)) - Mumulhl 127 | - add type - ([3005f19](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/3005f199d679ce05f357650f492f293f59a983e2)) - Mumulhl 128 | - cache bugs - ([36a24d2](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/36a24d20de1de2d0cc2e23e6665ff8099f1733b2)) - Mumulhl 129 | - delete console.log - ([184aa51](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/184aa51bbd57cf46bdbc34aca8c5372b2e7c1ca0)) - Mumulhl 130 | - import specifier - ([75330fc](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/75330fc1b7eb4883ba308b29d57e1504e3af0b49)) - Mumulhl 131 | - import specifer - ([9d36f64](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/9d36f6478b7cd5f99b510a2c0a705a74c6eb4a44)) - Mumulhl 132 | - delete deploy from deno.json - ([109b6ae](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/109b6aecebbe6000b10a4177e26e9ca3ffd98dd5)) - Mumulhl 133 | - add --unstable to deno.json - ([d655e60](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/d655e6091ec280000b2369218cdbc9f4a91d1c69)) - Mumulhl 134 | 135 | ### Documentation 136 | 137 | - add deploy methods - ([42cada0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/42cada05b78b0041c0552cd8e9f7a4be87752f82)) - Mumulhl 138 | - add clone repo - ([c86cae6](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/c86cae6d19b25ebd7ed24754bc10ff3619b13116)) - Mumulhl 139 | - add Deno Deploy web - ([4b04486](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/4b04486a09558fc5ffa51548fe99dcb82c8d93d9)) - Mumulhl 140 | - add configuration - ([26704b4](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/26704b451846d9de3387174c8a2b9ef4b7698f11)) - Mumulhl 141 | - add --unstable flag - ([fef67a1](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/fef67a193f8eebd3a795bd48c4f2b52afd23c955)) - Mumulhl 142 | 143 | ### Features 144 | 145 | - support stream - ([6c84ca4](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/6c84ca43a82a9339ea6fb2dfd4d522c3a329436d)) - Mumulhl 146 | - support vercel deploy - ([b16bf0f](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/b16bf0f4056c3656741ae513f1002e81380b090e)) - Mumulhl 147 | - add clean cache - ([09ff3b0](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/09ff3b031d5c1aa119d7361be497a4750cb301e6)) - Mumulhl 148 | 149 | ### Miscellaneous Chores 150 | 151 | - initialize - ([9339454](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/9339454908a9cf6d178bd73350fa520c65be36e5)) - Mumulhl 152 | - update deno.lock - ([fb5f8de](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/fb5f8de751d7a2a231bbd73f55c73200e4f2fcb5)) - Mumulhl 153 | - release v1.0.0 - ([f7a7b26](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/f7a7b2621cf768a51e0641fa904ec829d6a767c4)) - Mumulhl 154 | 155 | ### Revert 156 | 157 | - feat: support vercel deploy - ([700f166](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/700f16608a60f2203c09b1a5901f895fbb51b0a4)) - Mumulhl 158 | 159 | ### Style 160 | 161 | - optimize stream code - ([c352102](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/c35210280ef8db78cf3654947e0cdb6e2785b58b)) - Mumulhl 162 | - optimize stream code - ([b40d750](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/b40d7508823a5b44ae786b641f77a190cfc6ab0b)) - Mumulhl 163 | - optimize cache - ([e4724b1](https://github.com/mumu-lhl/duckduckgo-ai-chat-service/commit/e4724b137e5b69b6c702097baf78318c92c92498)) - Mumulhl 164 | 165 | 166 | --------------------------------------------------------------------------------