From f9ff19e393c3e545167408792a036e140dba2212 Mon Sep 17 00:00:00 2001 From: fatwang2 Date: Sun, 12 May 2024 13:06:15 +0800 Subject: [PATCH] support setting up on shortcut directly --- README.md | 27 ++++++++++++--------- src/chat.ts | 32 +++++++++++-------------- src/functions/index.ts | 3 +-- src/functions/search.ts | 3 ++- src/functions/weather.ts | 51 ---------------------------------------- wrangler.toml | 9 ++++--- 6 files changed, 37 insertions(+), 88 deletions(-) delete mode 100644 src/functions/weather.ts diff --git a/README.md b/README.md index c3d43d2..93cbbb2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,16 @@ This is a Siri Ultra that works with Apple Shortcuts removing the need for a ded The assistant is run on Cloudflare Workers and can work with any LLM model. The code snippet here uses llama 3 as an example with a custom function calling for fetching weather data and search online. -## Setting Up the Shortcut +# Usage + +## Method 1: Setting Up the Shortcut Directly +1. **Install the Shortcut**: + - Click [this link](https://search2ai.online/siri002) to install. + +2. **Configure**: + - Open the Shortcut, follow prompts to input necessary variables. + +## Method 2: Setting Up the Self-Hosted Version ### Getting Started @@ -21,25 +30,22 @@ The assistant is run on Cloudflare Workers and can work with any LLM model. The 4. **Create KV namespaces**: - Run `npx wrangler kv:namespace create chats` to create a KV namespace. Note down the ID. - - Run `npx wrangler kv:namespace create chats --preview` to create a preview KV namespace. Note down the ID. 5. **Configure the project**: - Update `wrangler.toml` with the namespace IDs: ```toml [[kv_namespaces]] - binding = "personal_ai_chats" + binding = "siri_ai_chats" id = "" - preview_id = "" ``` 6. **Set up API keys**: -- Run `npx wrangler secret put API_KEY` to set the GROQ or OpenAI API key. -- Run `npx wrangler secret put OPENWEATHERMAP_API_KEY` to set the OpenWeather API key. -- Run `npx wrangler secret put SEARCH1API_KEY` to set the SEARCH1API_KEY API key. +- Run `npx wrangler secret put API_KEY` to set the [Groq](https://console.groq.com/login) or [OpenAI](https://openai.com/) API key. +- Run `npx wrangler secret put SEARCH1API_KEY` to set the [Search1API](https://www.search1api.com/) API key. - > **Note**: You can get these keys by signing up on [GroqCloud](https://console.groq.com/login) or [OpenAI](https://openai.com/) and [OpenWeather](https://home.openweathermap.org/users/sign_up) and [Search1API](https://www.search1api.com/) respectively. + > **Note**: You can only set API_KEY if you don't need search function 7. **Update the LLMs Vars**: ```toml @@ -49,7 +55,6 @@ The assistant is run on Cloudflare Workers and can work with any LLM model. The SYSTEM_PROMPT="You are Siri Pro. Answer in 1-2 sentences. Be friendly, helpful and concise. Default to metric units when possible. Keep the conversation short and sweet. You only answer in text. Don't include links or any other extras. Don't respond with computer code, for example don't return user longitude." ``` - ### Deploying the Worker To deploy the worker, run `npx wrangler deploy`. @@ -57,8 +62,8 @@ To deploy the worker, run `npx wrangler deploy`. ### Setting Up the Shortcut 1. **Install the shortcut**: - - Use [this link](https://www.icloud.com/shortcuts/284c0f68f7b0450ebab0b19e9adc317f) to install the shortcut. + - Use [this link](https://search2ai.online/siri002) to install the shortcut. 2. **Configure the shortcut**: - Open the shortcut and replace the `URL` field with your worker's URL. - - If you didn't change the default name, the URL should be `https://personal-ai..workers.dev`. + - If you didn't change the default name, the URL should be `https://siri-ultra..workers.dev`. diff --git a/src/chat.ts b/src/chat.ts index 50dd3cb..548bfa8 100644 --- a/src/chat.ts +++ b/src/chat.ts @@ -6,52 +6,51 @@ export interface IBody { chat_id: string; input: string; date: string; - location: { - latitude: number; - longitude: number; + config?: { + model?: string; + api_base?: string; + system_prompt?: string; + api_key?: string; + search1api_key?: string; }; } + export interface IRequest { env: any; request: IBody; } export const getClient = (req: IRequest): { client: OpenAI; model: string } => { - const url = req.env.API_BASE || "https://api.groq.com/openai/v1/"; - const client = new OpenAI({ - apiKey: req.env.API_KEY, - }); + const url = req.request.config?.api_base || req.env.API_BASE || "https://api.groq.com/openai/v1/"; + const apiKey = req.request.config?.api_key || req.env.API_KEY; + const client = new OpenAI({ apiKey }); client.baseURL = url; - const model = req.env.MODEL || "llama3-70b-8192"; + const model = req.request.config?.model || req.env.MODEL || "llama3-70b-8192"; return { client, model }; }; export const handle = async (req: IRequest): Promise => { const openai = getClient(req); const defaultSystemPrompt = ` - You are Siri Pro. Answer in 1-2 sentences. Be friendly, helpful and concise. + You are Siri Ultra. Answer in 1-2 sentences. Be friendly, helpful and concise. Default to metric units when possible. Keep the conversation short and sweet. You only answer in text. Don't include links or any other extras. Don't respond with computer code, for example don't return user longitude. `; - const customSystemPrompt = req.env.SYSTEM_PROMPT || defaultSystemPrompt; + const customSystemPrompt = req.request.config?.system_prompt || req.env.SYSTEM_PROMPT || defaultSystemPrompt; const system = ` ${customSystemPrompt} User's current info: date: ${req.request.date} - lat:${req.request.location.latitude}, lon:${req.request.location.longitude} `; - console.log("system", system); - - const chat = ChatHistory.getInstance(req.env.personal_ai_chats); + const chat = ChatHistory.getInstance(req.env.siri_ai_chats); await chat.add(req.request.chat_id, { role: "user", content: req.request.input, }); - let response = ""; while (true) { const ask = await openai.client.chat.completions.create({ @@ -62,8 +61,6 @@ export const handle = async (req: IRequest): Promise => { ], tools: FunctionHandler.functions, }); - console.log("ask", JSON.stringify(ask, null, 2)); - if (ask.choices[0].message.tool_calls) { chat.add(req.request.chat_id, { role: "assistant", @@ -76,7 +73,6 @@ export const handle = async (req: IRequest): Promise => { JSON.parse(tool.function.arguments), req ); - console.log("result", result); await chat.add(req.request.chat_id, { role: "tool", tool_call_id: tool.id, diff --git a/src/functions/index.ts b/src/functions/index.ts index bab08f3..8e73b04 100644 --- a/src/functions/index.ts +++ b/src/functions/index.ts @@ -1,9 +1,8 @@ -import { weather } from "./weather"; import { search } from "./search"; import { IFunction } from "./type"; import { IRequest } from "../chat"; -const functions: IFunction[] = [weather, search]; +const functions: IFunction[] = [search]; const handle = async ( name: string, diff --git a/src/functions/search.ts b/src/functions/search.ts index a07ad89..230b8f6 100644 --- a/src/functions/search.ts +++ b/src/functions/search.ts @@ -51,6 +51,7 @@ export const search: IFunction = { }, }, async execute(args: any, req: IRequest) { - return await searchWeb(args.query, req.env.SEARCH1API_KEY); + const search1apiKey = req.request.config?.search1api_key || req.env.SEARCH1API_KEY; + return await searchWeb(args.query, search1apiKey); }, }; \ No newline at end of file diff --git a/src/functions/weather.ts b/src/functions/weather.ts deleted file mode 100644 index 684b96f..0000000 --- a/src/functions/weather.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { IFunction } from "./type"; -import { IRequest } from "../chat"; - -const BASE_URL = "https://api.openweathermap.org/data/2.5/weather"; - -export const getWeather = async ( - lon: number, - lat: number, - apiKey: string -): Promise => { - const url = `${BASE_URL}?lat=${lat}&lon=${lon}&appid=${apiKey}`; - try { - const response = await fetch(url); - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - const data = await response.json(); - return JSON.stringify(data); - } catch (error) { - console.error("Failed to fetch weather data:", error); - return JSON.stringify({ error: "Failed to fetch weather data" }); - } -}; - -export const weather: IFunction = { - type: "function", - function: { - name: "get_weather", - description: "Get the current weather", - parameters: { - type: "object", - properties: { - longitude: { - type: "number", - description: "The longitude to get the weather for", - }, - latitude: { - type: "number", - description: "The latitude to get the weather for", - }, - }, - }, - }, - async execute(args: any, req: IRequest) { - return await getWeather( - args.longitude, - args.latitude, - req.env.OPENWEATHERMAP_API_KEY - ); - }, -}; diff --git a/wrangler.toml b/wrangler.toml index 4c04237..890121c 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,4 +1,4 @@ -name="personal-ai" +name="siri-ultra" main="src/index.ts" compatibility_date = "2022-11-22" @@ -7,9 +7,8 @@ compatibility_date = "2022-11-22" [vars] API_BASE= "https://api.groq.com/openai/v1/" MODEL="llama3-70b-8192" -SYSTEM_PROMPT="You are Siri Pro. Answer in 1-2 sentences. Be friendly, helpful and concise. Default to metric units when possible. Keep the conversation short and sweet. You only answer in text. Don't include links or any other extras. Don't respond with computer code, for example don't return user longitude." +SYSTEM_PROMPT="You are Siri Ultra. Answer in 1-2 sentences. Be friendly, helpful and concise. Default to metric units when possible. Keep the conversation short and sweet. You only answer in text. Don't include links or any other extras. Don't respond with computer code, for example don't return user longitude." [[kv_namespaces]] -binding = "personal_ai_chats" -id = "" -preview_id = "" +binding = "siri_ai_chats" +id = "d9afe4035eec4d33964b7fdb1bf9496d" \ No newline at end of file