而且作为十多年来的标准,您可以立即将这样的计算部署为 Wolfram Cloud 中的 Web API——可通过 Web 浏览器、外部程序等立即访问:
但是现在有一种新的部署形式:作为ChatGPT的插件。首先,您需要获取插件工具包:
然后您就可以立即部署您的插件了。只需要:
最后一步是您需要告诉ChatGPT有关您的插件。在Web界面中(按照当前配置),选择“Plugins > Plugin store > Develop your own plugin”(“插件”>“插件商店”>“开发自己的插件”),并将来自ChatGPTPluginDeployment的URL插入到你的对话框中 (通过点击复制按钮获取 ) :
ChatGPT 成功调用了插件,并返回了一张图片。有趣的是,它猜测(事实上是正确的)“地理影响圆盘”应该是什么意思。请记住,它无法看到图片或读取代码,因此它的猜测只能基于 API 函数名称和提出的问题。当然,在调用 API 函数时,它必须有效地理解至少一点内容——x 应该表示位置,而 radius 则表示距离。
好吧,让我们来谈谈本地插件部署。ChatGPTPluginDeploy 在您的计算机上有效地设置了一个最小化 Web 服务器(使用10行 Wolfram 语言代码实现),在 ChatGPTPluginDeploy 选择的端口上运行,并且每当它收到对 API URL 的请求时调用 Wolfram 引擎与您的 API 函数进行交互。
好的,但是ChatGPT怎么知道你的API呢?首先,您需要通过ChatGPT UI的 Plugins > Plugin store > Develop your own plugin(插件>插件商店>开发自己的插件)告诉它您正在使用的端口。您可以通过单击ChatGPTPluginDeployment对象中的 图标或以编程方式找到端口:
本文为《Enabling conversational interaction on mobile with LLMs》的中文翻译稿,Google发布的这篇文章对大语言模型在应用侧的发展有很积极的意义。LLM目前还是聚焦在文本上,因为是语言模型啊。但是这篇文章在研究的是如何让LLM看懂UI,并可以和文本内容进行转化相互转换:比如,看到UI内容进行提问、理解屏幕展现的主要内容是什么、让大模型看图阅读理解,还有一个就是像人一样去操作(这个我觉得是RPA的增强版)。
defyour_function(a, b): let c = a c = b # error: c is immutable
if c != b: var c = b stuff()
let和var声明支持类型说明符以及模式,还支持延迟初始化:
1 2 3 4 5 6 7 8 9 10
defyour_function(): let x: Int8 = 42 let y: Int64 = 17
let z: Int8 if x != 0: z = 1 else: z = foo() use(z)
Note that let and var are completely opt-in when in def declarations. You can still use implicitly declared values as with Python, and they get function scope as usual.
structMyPair: var first: Int var second: Int def __init__(self&, first: Int, second: Int): self.first = first self.second = second def __lt__(self, rhs: MyPair) -> Bool: returnself.first < rhs.first or (self.first == rhs.first and self.second < rhs.second)
从语法上讲,与 Python 类相比,结构体中的所有实例属性都必须使用 var 或 let 声明显式声明。
if ('serviceWorker'in navigator) { addEventListener('load', function () { navigator.serviceWorker.register('./path/to/service-worker.js'); }); }
深入service worker
在 service worker 中,您可以访问 $service-worker 模块,该模块为您提供所有静态资源、构建文件和预渲染页面的路径。还提供了一个应用程序版本字符串,您可以使用它来创建唯一的缓存名称以及部署的base路径。如果您的 Vite 配置指定了 define(用于全局变量替换),则这也将应用于服务工作者以及服务器/客户端构建。
// Create a unique cache name for this deployment constCACHE = `cache-${version}`;
constASSETS = [ ...build, // the app itself ...files // everything in `static` ];
self.addEventListener('install', (event) => { // Create a new cache and add all files to it asyncfunctionaddFilesToCache() { const cache = await caches.open(CACHE); await cache.addAll(ASSETS); }
event.waitUntil(addFilesToCache()); });
self.addEventListener('activate', (event) => { // Remove previous cached data from disk asyncfunctiondeleteOldCaches() { for (const key ofawait caches.keys()) { if (key !== CACHE) await caches.delete(key); } }
event.waitUntil(deleteOldCaches()); });
self.addEventListener('fetch', (event) => { // ignore POST requests etc if (event.request.method !== 'GET') return;
{ "exports":{ ".":{ "types":"./dist/index.d.ts", // changing `svelte` to `default` is a breaking change: "svelte":"./dist/index.js" "default":"./dist/index.js" }, // removing this is a breaking change: "./foo":{ "types":"./dist/foo.d.ts", "svelte":"./dist/foo.js", "default":"./dist/foo.js" }, // adding this is ok: "./bar":{ "types":"./dist/bar.d.ts", "svelte":"./dist/bar.js", "default":"./dist/bar.js" } } }
使用 npm i -D @sveltejs/adapter-node 进行安装,然后将适配器添加到您的svelte.config.js文件中:
svelte.config.js
1 2 3 4 5 6 7
import adapter from'@sveltejs/adapter-node';
exportdefault { kit: { adapter: adapter() } };
Deploying
首先,使用npm run build构建您的应用程序。这将在适配器选项中指定的输出目录(默认为build)中创建生产服务器。
要运行应用程序,您需要输出目录、项目的package.json文件以及node_modules中的生产依赖项。可以通过复制package.json和package-lock.json并运行npm ci --omit dev来生成生产依赖项(如果您的应用程序没有任何依赖项,则可以跳过此步骤)。然后,您可以使用以下命令启动应用程序:
使用npm i -D @sveltejs/adapter-static进行安装,然后将适配器添加到您的 svelte.config.js 文件中:
svelte.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import adapter from'@sveltejs/adapter-static';
exportdefault { kit: { adapter: adapter({ // default options are shown. On some platforms // these options are set automatically — see below pages: 'build', assets: 'build', fallback: null, precompress: false, strict: true }) } };
…and add the prerender option to your root layout:
…并将prerender 选项添加到您的根布局中:
src/routes/+layout.js
1 2
// This can be false if you're using a fallback (i.e. SPA mode) export const prerender = true;
You must ensure SvelteKit’s trailingSlash option is set appropriately for your environment. If your host does not render /a.html upon receiving a request for /a then you will need to set trailingSlash: 'always' to create /a/index.html instead.
# If you're using pnpm, add this step then change the commands and cache key below to use `pnpm` # - name: Install pnpm # uses: pnpm/action-setup@v2 # with: # version: 8
使用npm i -D @sveltejs/adapter-netlify进行安装,然后将适配器添加到您的 svelte.config.js 文件中:
svelte.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import adapter from'@sveltejs/adapter-netlify';
exportdefault { kit: { // default options are shown adapter: adapter({ // if true, will create a Netlify Edge Function rather // than using standard Node-based functions edge: false,
// if true, will split your app into multiple functions // instead of creating a single one for the entire app. // if `edge` is true, this option cannot be used split: false }) } };
exportdefault { kit: { adapter: adapter({ // will create a Netlify Edge Function using Deno-based // rather than using standard Node-based functions edge: true }) } };
exportconstload = async (event) => { const context = event.platform.context; console.log(context); // shows up in your functions log in the Netlify app };
exportconst config = { isr: { // Expiration time (in seconds) before the cached asset will be re-generated by invoking the Serverless Function. // Setting the value to `false` means it will never expire. expiration: 60,
// Random token that can be provided in the URL to bypass the cached version of the asset, by requesting the asset // with a __prerender_bypass=<token> cookie. // // Making a `GET` or `HEAD` request with `x-prerender-revalidate: <token>` will force the asset to be re-validated. bypassToken: BYPASS_TOKEN,
// List of valid query parameters. Other parameters (such as utm tracking codes) will be ignored, // ensuring that they do not result in content being regenerated unnecessarily allowQuery: ['search'] } };
<script> let a = 0; let b = 0; let total = 0; asyncfunctionadd() { const response = awaitfetch('/api/add', { method: 'POST', body: JSON.stringify({ a, b }), headers: { 'content-type': 'application/json' } }); total = await response.json(); } </script>
<script> import { page } from'$app/stores'; /** @type {import('./$types').PageData} */ exportlet data; // we can access `data.posts` because it's returned from // the parent layout `load` function $: index = data.posts.findIndex(post => post.slug === $page.params.slug); $: next = data.posts[index - 1]; </script>
// cache the page for the same length of time // as the underlying data setHeaders({ age: response.headers.get('age'), 'cache-control': response.headers.get('cache-control') });
/** @type {import('./$types').PageLoad} */ exportasyncfunctionload({ fetch, depends }) { // load reruns when `invalidate('https://api.example.com/random-number')` is called... const response = awaitfetch('https://api.example.com/random-number');
// ...or when `invalidate('app:random')` is called depends('app:random');
return { number: await response.json() }; }
src/routes/random-number/+page.svelte
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<script> import { invalidate, invalidateAll } from '$app/navigation'; /** @type {import('./$types').PageData} */ export let data;
function rerunLoadFunction() { // any of these will cause the `load` function to re-run invalidate('app:random'); invalidate('https://api.example.com/random-number'); invalidate(url => url.href.includes('random-number')); invalidateAll(); } </script>
<p>random number: {data.number}</p> <button on:click={rerunLoadFunction}>Update random number</button>
const user = await db.getUser(email); cookies.set('sessionid', await db.createSession(user));
return { success: true }; }, register: async (event) => { // TODO register the user } };
src/routes/login/+page.svelte
1 2 3 4 5 6 7 8 9 10 11 12
<script> /** @type {import('./$types').PageData} */ export let data; /** @type {import('./$types').ActionData} */ export let form; </script>
{#if form?.success} <!-- this message is ephemeral; it exists because the page was rendered in response to a form submission. it will vanish if the user reloads --> <p>Successfully logged in! Welcome back, {data.user.name}</p> {/if}
<form method="POST" use:enhance={({ form, data, action, cancel, submitter }) => { // `form` is the `<form>` element // `data` is its `FormData` object // `action` is the URL to which the form is posted // `cancel()` will prevent the submission // `submitter` is the `HTMLElement` that caused the form to be submitted
return async ({ result, update }) => { // `result` is an `ActionResult` object // `update` is a function which triggers the logic that would be triggered if this callback wasn't set }; }} >
<script> import { enhance, applyAction } from '$app/forms';
/** @type {import('./$types').ActionData} */ export let form; </script>
<form method="POST" use:enhance={({ form, data, action, cancel }) => { // `form` is the `<form>` element // `data` is its `FormData` object // `action` is the URL to which the form is posted // `cancel()` will prevent the submission
return async ({ result }) => { // `result` is an `ActionResult` object if (result.type === 'error') { await applyAction(result); } }; }} >
通常情况下,SvelteKit 首先在服务器上呈现您的页面,并将该 HTML 发送到客户端进行水合作用。如果您将 ssr 设置为 false,则会呈现一个空的“外壳”页面。如果您的页面无法在服务器上呈现(例如,因为使用了仅限于浏览器的全局变量,如 document),则这很有用,但在大多数情况下不建议这样做(请参见附录)。
+page.js
1
export const ssr = false;
如果你在根 +layout.js 中添加 export const ssr = false,那么整个应用程序将只在客户端渲染 —— 这基本上意味着你把应用程序变成了单页应用。
你可能会想知道,如果我们不能使用自己的存储库,我们如何能够使用 $page.data 和其他应用商店。答案是服务器上的应用商店使用 Svelte 的上下文 API - 存储库附加到组件树中,并通过setContext进行订阅时检索 getContext。我们可以对自己的存储库执行相同的操作:
src/routes/+layout.svelte
1 2 3 4 5 6 7 8
<script> import { setContext } from 'svelte'; import { writable } from 'svelte/store'; /** @type {import('./$types').LayoutData} */ export let data; // Create a store and update it when necessary... const user = writable(); $: user.set(data.user); // ...and add it to the context for child components to access setContext('user', user); </script>
src/routes/user/+page.svelte
1 2 3 4 5 6
<script> import { getContext } from 'svelte'; // Retrieve user store from context const user = getContext('user'); </script>