mirror of
https://github.com/runebookai/tome.git
synced 2025-07-21 00:27:30 +03:00
Back to line length of 100 + some svelte/ts settings
- Goes back to a max line length of 100
- Makes whitespace insignificant in Svelte files (see below)
- Avoids parens around single argument closures, in TS, when unncessary
*Whitespace Significance*
In HTML, `<p><b> 1 </b></p>` and `<p><b>1</b></p>` are not the same
thing. In the former, the "1" has spaces around it. If you were to try
to split this into multiple lines...
```html
<p>
<b>
1
</b>
</p>
```
...you would lose that whitespace. The newlines reset any significance
around individual tokens. This meant prettier would format that code
as...
```html
<p>
<b
> 1 </b>
</p>
```
...which is insane and hideous.
We're saying all whitespace is insignificant from now on. Meaning
prettier no longer needs to retain it and can format that code as a sane
person.
This means you need to explicitly use ` ` characters if you
explicitly need whitespace around things. OR put it in a `span` and use
css.
TL;DR: do not rely on whitespace significance in HTML.
This commit is contained in:
@@ -4,7 +4,8 @@
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 80,
|
||||
"printWidth": 100,
|
||||
"arrowParens": "avoid",
|
||||
"plugins": [
|
||||
"prettier-plugin-svelte",
|
||||
"prettier-plugin-tailwindcss"
|
||||
@@ -14,7 +15,7 @@
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte",
|
||||
"printWidth": 100
|
||||
"htmlWhitespaceSensitivity": "ignore"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
></path>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m24 12h-16c-.553 0-1-.448-1-1s.447-1 1-1h16c.553 0 1 .448 1 1s-.447 1-1 1z"
|
||||
<path
|
||||
d="m24 12h-16c-.553 0-1-.448-1-1s.447-1 1-1h16c.553 0 1 .448 1 1s-.447 1-1 1z"
|
||||
></path>
|
||||
</g>
|
||||
<g>
|
||||
<path d="m16 16h-8c-.553 0-1-.448-1-1s.447-1 1-1h8c.553 0 1 .448 1 1s-.447 1-1 1z"
|
||||
<path
|
||||
d="m16 16h-8c-.553 0-1-.448-1-1s.447-1 1-1h8c.553 0 1 .448 1 1s-.447 1-1 1z"
|
||||
></path>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
|
Before Width: | Height: | Size: 946 B After Width: | Height: | Size: 978 B |
@@ -4,8 +4,8 @@
|
||||
width="100%"
|
||||
height="100%"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
><path
|
||||
>
|
||||
<path
|
||||
d="m2 12c0-1.8129.44444-3.48538 1.33333-5.01754.90059-1.53217 2.11696-2.74269 3.64913-3.63158 1.54386-.90059 3.21634-1.35088 5.01754-1.35088 1.8129 0 3.4854.45029 5.0175 1.35088 1.5322.88889 2.7427 2.10526 3.6316 3.64912.9006 1.53216 1.3509 3.1988 1.3509 5s-.4503 3.4737-1.3509 5.0175c-.8889 1.5322-2.1052 2.7486-3.6491 3.6492-1.5322.8889-3.1988 1.3333-5 1.3333s-3.47368-.4444-5.01754-1.3333c-1.53217-.9006-2.74854-2.117-3.64913-3.6492-.88889-1.5438-1.33333-3.2163-1.33333-5.0175zm4.50877 0c0 .2807.09942.5146.29825.7018l2.89473 2.8947c.22223.2105.49125.3158.80705.3158.3275 0 .5965-.1053.807-.3158l5.8947-5.89475c.1989-.17543.2983-.40935.2983-.70175 0-.2807-.0994-.51462-.2983-.70175-.1871-.19884-.421-.29825-.7017-.29825s-.5146.09941-.7018.29825l-5.2982 5.28065-2.29827-2.2807c-.17544-.1988-.40936-.2982-.70176-.2982-.269 0-.50292.0994-.70175.2982-.19883.1872-.29825.4211-.29825.7018z"
|
||||
>
|
||||
</path>
|
||||
></path>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -41,7 +41,7 @@
|
||||
}
|
||||
|
||||
async function remove(key: string) {
|
||||
env = env.filter((e) => e[0] !== key);
|
||||
env = env.filter(e => e[0] !== key);
|
||||
await save();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
<Flex bind:this={ref} class="bg-medium relative h-16 w-full hover:cursor-pointer">
|
||||
<Flex
|
||||
onclick={(e) => toggle(e)}
|
||||
onclick={e => toggle(e)}
|
||||
class="border-light absolute top-0 left-0 w-full justify-between
|
||||
rounded-md border p-2 px-4"
|
||||
>
|
||||
|
||||
@@ -60,13 +60,13 @@
|
||||
}
|
||||
|
||||
function isValid() {
|
||||
return config.every((prop) => prop.valid);
|
||||
return config.every(prop => prop.valid);
|
||||
}
|
||||
|
||||
function validateAll() {
|
||||
config
|
||||
.filter((c) => c.required)
|
||||
.forEach((prop) => {
|
||||
.filter(c => c.required)
|
||||
.forEach(prop => {
|
||||
prop.valid = validate(prop.value);
|
||||
});
|
||||
}
|
||||
@@ -115,8 +115,13 @@
|
||||
<svelte:window onmessage={onMessage} />
|
||||
|
||||
{#if srcdoc}
|
||||
<iframe title="stdioFunction" class="hidden h-0 w-0" sandbox="allow-scripts" allow="" {srcdoc}>
|
||||
</iframe>
|
||||
<iframe
|
||||
title="stdioFunction"
|
||||
class="hidden h-0 w-0"
|
||||
sandbox="allow-scripts"
|
||||
allow=""
|
||||
{srcdoc}
|
||||
></iframe>
|
||||
{/if}
|
||||
|
||||
{#if server && config}
|
||||
@@ -126,7 +131,7 @@
|
||||
|
||||
{#if config.length > 0}
|
||||
<div class="w-full px-8">
|
||||
{#each config.filter((p) => p.required) as prop (prop.name)}
|
||||
{#each config.filter(p => p.required) as prop (prop.name)}
|
||||
<Input
|
||||
label={prop.name}
|
||||
name={prop.name}
|
||||
@@ -137,7 +142,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if config.some((p) => !p.required)}
|
||||
{#if config.some(p => !p.required)}
|
||||
<Flex class="mt-8 w-full flex-col items-start">
|
||||
<button
|
||||
onclick={() => toggleOptional()}
|
||||
@@ -148,7 +153,7 @@
|
||||
|
||||
{#if optionalIsOpen}
|
||||
<div class="w-full px-8">
|
||||
{#each config.filter((p) => !p.required) as prop (prop.name)}
|
||||
{#each config.filter(p => !p.required) as prop (prop.name)}
|
||||
<Input
|
||||
label={prop.name}
|
||||
name={prop.name}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
async function install() {
|
||||
update = await availableUpdate();
|
||||
|
||||
await update?.downloadAndInstall((event) => {
|
||||
await update?.downloadAndInstall(event => {
|
||||
switch (event.event) {
|
||||
case 'Started':
|
||||
totalDownload = (event.data.contentLength as number) / 1000.0;
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<h1 class="text-purple text-3xl">Welcome to Tome</h1>
|
||||
<p>
|
||||
Thanks for being an early adopter! We appreciate you kicking the tires of our
|
||||
<strong>Technical Preview</strong> as we explore making local LLMs, MCP, and AI app composition
|
||||
a better experience.
|
||||
<strong>Technical Preview</strong>
|
||||
as we explore making local LLMs, MCP, and AI app composition a better experience.
|
||||
</p>
|
||||
<p>
|
||||
This is an extremely early build. There will be problems – edges are rough – features
|
||||
|
||||
@@ -39,19 +39,13 @@ export const init: ClientInit = async () => {
|
||||
await Config.migrate();
|
||||
info('[green]✔ config migrated');
|
||||
|
||||
await startup.addCheck(
|
||||
StartupCheck.Agreement,
|
||||
async () => Config.agreedToWelcome,
|
||||
);
|
||||
await startup.addCheck(StartupCheck.Agreement, async () => Config.agreedToWelcome);
|
||||
|
||||
await startup.addCheck(
|
||||
StartupCheck.UpdateAvailable,
|
||||
async () => await isUpToDate(),
|
||||
);
|
||||
await startup.addCheck(StartupCheck.UpdateAvailable, async () => await isUpToDate());
|
||||
|
||||
await startup.addCheck(
|
||||
StartupCheck.NoModels,
|
||||
async () => Engine.all().flatMap(e => e.models).length > 0,
|
||||
async () => Engine.all().flatMap(e => e.models).length > 0
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export enum DeepLinks {
|
||||
}
|
||||
|
||||
export function setupDeeplinks() {
|
||||
listen<string>(DeepLinks.InstallMcpServer, async (event) => {
|
||||
listen<string>(DeepLinks.InstallMcpServer, async event => {
|
||||
goto(`/mcp-servers/install?config=${event.payload}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class Gemini implements Client {
|
||||
tools?: Tool[],
|
||||
options?: Options
|
||||
): Promise<IMessage> {
|
||||
const messages = history.map((m) => GeminiMessage.from(m)).compact();
|
||||
const messages = history.map(m => GeminiMessage.from(m)).compact();
|
||||
|
||||
let config: GenerateContentConfig = {
|
||||
temperature: options?.temperature,
|
||||
@@ -50,7 +50,7 @@ export default class Gemini implements Client {
|
||||
let toolCalls: ToolCall[] = [];
|
||||
|
||||
if (functionCalls) {
|
||||
toolCalls = functionCalls.map((tc) => ({
|
||||
toolCalls = functionCalls.map(tc => ({
|
||||
function: {
|
||||
name: tc.name as string,
|
||||
arguments: tc.args || {},
|
||||
@@ -69,8 +69,8 @@ export default class Gemini implements Client {
|
||||
|
||||
async models(): Promise<IModel[]> {
|
||||
return (await this.client.models.list()).page
|
||||
.filter((model) => this.supportedModels.includes(model.name as string))
|
||||
.map((model) => {
|
||||
.filter(model => this.supportedModels.includes(model.name as string))
|
||||
.map(model => {
|
||||
const metadata = model;
|
||||
const name = metadata.name?.replace('models/', '') as string;
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ function fromToolResponse(message: IMessage): Content {
|
||||
// Find the `toolCall` message for this response
|
||||
const session = Session.find(message.sessionId as number);
|
||||
const messages = Session.messages(session);
|
||||
const call = messages.flatMap((m) => m.toolCalls).find((tc) => tc.id == message.toolCallId);
|
||||
const call = messages.flatMap(m => m.toolCalls).find(tc => tc.id == message.toolCallId);
|
||||
|
||||
return {
|
||||
role: 'user',
|
||||
|
||||
@@ -32,7 +32,7 @@ function from(tools: Tool | Tool[]): ToolListUnion {
|
||||
}
|
||||
|
||||
function fromMany(tools: Tool[]): FunctionDeclaration[] {
|
||||
return tools.map((tool) => fromOne(tool));
|
||||
return tools.map(tool => fromOne(tool));
|
||||
}
|
||||
|
||||
function fromOne(tool: Tool): FunctionDeclaration {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class Ollama implements Client {
|
||||
tools: Tool[] = [],
|
||||
options: Options = {}
|
||||
): Promise<IMessage> {
|
||||
const messages = history.map((m) => this.message.from(m));
|
||||
const messages = history.map(m => this.message.from(m));
|
||||
const response = await this.client.chat({
|
||||
model: model.name,
|
||||
messages,
|
||||
@@ -57,9 +57,7 @@ export default class Ollama implements Client {
|
||||
async models(): Promise<IModel[]> {
|
||||
const models = (await this.client.list()).models;
|
||||
|
||||
return Promise.all(
|
||||
models.map(async (model) => await this.info(model.name))
|
||||
);
|
||||
return Promise.all(models.map(async model => await this.info(model.name)));
|
||||
}
|
||||
|
||||
async info(name: string): Promise<IModel> {
|
||||
|
||||
@@ -10,7 +10,7 @@ export function from(message: IMessage): Message {
|
||||
return {
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
tool_calls: message.toolCalls?.map((c) => ({
|
||||
tool_calls: message.toolCalls?.map(c => ({
|
||||
function: {
|
||||
name: c.function.name,
|
||||
arguments: c.function.arguments,
|
||||
|
||||
@@ -33,7 +33,7 @@ export default class OpenAI implements Client {
|
||||
tools: Tool[] = [],
|
||||
options: Options = {}
|
||||
): Promise<IMessage> {
|
||||
const messages = history.map((m) => OpenAiMessage.from(m));
|
||||
const messages = history.map(m => OpenAiMessage.from(m));
|
||||
const response = await this.client.chat.completions.create({
|
||||
model: model.name,
|
||||
messages,
|
||||
@@ -45,7 +45,7 @@ export default class OpenAI implements Client {
|
||||
let toolCalls: ToolCall[] = [];
|
||||
|
||||
if (tool_calls) {
|
||||
toolCalls = tool_calls.map((tc) => ({
|
||||
toolCalls = tool_calls.map(tc => ({
|
||||
function: {
|
||||
name: tc.function.name,
|
||||
arguments: JSON.parse(tc.function.arguments),
|
||||
@@ -66,10 +66,10 @@ export default class OpenAI implements Client {
|
||||
let allModels = (await this.client.models.list()).data;
|
||||
|
||||
if (this.supportedModels !== 'all') {
|
||||
allModels = allModels.filter((model) => this.supportedModels.includes(model.id));
|
||||
allModels = allModels.filter(model => this.supportedModels.includes(model.id));
|
||||
}
|
||||
|
||||
return allModels.map((model) => {
|
||||
return allModels.map(model => {
|
||||
const { id, ...metadata } = model;
|
||||
const name = id.replace('models/', ''); // Gemini model ids are prefixed with "model/"
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ function toolCalls(message: IMessage): OpenAI.ChatCompletionMessageToolCall[] |
|
||||
return;
|
||||
}
|
||||
|
||||
return message.toolCalls.map((call) => ({
|
||||
return message.toolCalls.map(call => ({
|
||||
id: call.id as string,
|
||||
type: 'function',
|
||||
function: {
|
||||
|
||||
@@ -93,7 +93,7 @@ Array.prototype.findBy = function <T extends Obj>(
|
||||
key: string,
|
||||
value: any
|
||||
): T | undefined {
|
||||
return this.find((item) => item[key] == value);
|
||||
return this.find(item => item[key] == value);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -106,5 +106,5 @@ Array.prototype.findBy = function <T extends Obj>(
|
||||
* ```
|
||||
*/
|
||||
Array.prototype.compact = function <T>(this: T[]): Exclude<T, undefined>[] {
|
||||
return this.filter((i) => i !== undefined) as Exclude<T, undefined>[];
|
||||
return this.filter(i => i !== undefined) as Exclude<T, undefined>[];
|
||||
};
|
||||
|
||||
@@ -13,7 +13,10 @@ export interface HttpOptions extends RequestInit {
|
||||
}
|
||||
|
||||
export async function fetch(url: string | URL | Request, options?: RequestInit) {
|
||||
const response: globalThis.Response = await invoke('fetch', { url, options });
|
||||
const response: globalThis.Response = await invoke('fetch', {
|
||||
url,
|
||||
options,
|
||||
});
|
||||
const { body, ...init } = response;
|
||||
return new globalThis.Response(body, init);
|
||||
}
|
||||
@@ -37,7 +40,10 @@ export abstract class HttpClient {
|
||||
async post<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
|
||||
const raw = Object.remove(options, 'raw');
|
||||
const raise: boolean | undefined = Object.remove(options, 'raise');
|
||||
const response = await this.request(uri, { ...options, method: 'POST' });
|
||||
const response = await this.request(uri, {
|
||||
...options,
|
||||
method: 'POST',
|
||||
});
|
||||
return raw ? response : await this.parse(response, raise);
|
||||
}
|
||||
|
||||
@@ -51,14 +57,20 @@ export abstract class HttpClient {
|
||||
async delete<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
|
||||
const raw = Object.remove(options, 'raw');
|
||||
const raise: boolean | undefined = Object.remove(options, 'raise');
|
||||
const response = await this.request(uri, { ...options, method: 'DELETE' });
|
||||
const response = await this.request(uri, {
|
||||
...options,
|
||||
method: 'DELETE',
|
||||
});
|
||||
return raw ? response : await this.parse(response, raise);
|
||||
}
|
||||
|
||||
async head<T>(uri: string, options: HttpOptions = {}): Promise<Response<T>> {
|
||||
const raw = Object.remove(options, 'raw');
|
||||
const raise: boolean | undefined = Object.remove(options, 'raise');
|
||||
const response = await this.request(uri, { ...options, method: 'HEAD' });
|
||||
const response = await this.request(uri, {
|
||||
...options,
|
||||
method: 'HEAD',
|
||||
});
|
||||
return raw ? response : await this.parse(response, raise);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@ export class OllamaClient extends HttpClient {
|
||||
stream: false,
|
||||
});
|
||||
|
||||
const response = (await this.post('/api/chat', { body })) as OllamaResponse;
|
||||
const response = (await this.post('/api/chat', {
|
||||
body,
|
||||
})) as OllamaResponse;
|
||||
|
||||
let thought: string | undefined;
|
||||
let content: string = response.message.content
|
||||
@@ -75,7 +77,12 @@ export class OllamaClient extends HttpClient {
|
||||
|
||||
async connected(): Promise<boolean> {
|
||||
return (
|
||||
((await this.get('', { raw: true, timeout: 500 })) as globalThis.Response).status == 200
|
||||
(
|
||||
(await this.get('', {
|
||||
raw: true,
|
||||
timeout: 500,
|
||||
})) as globalThis.Response
|
||||
).status == 200
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ function _log(args: any[], level: Level) {
|
||||
if (typeof args[0] !== 'string') {
|
||||
console[level](...fmt(level, ''), ...args);
|
||||
} else {
|
||||
console[level](...args.flatMap((arg) => (typeof arg === 'string' ? fmt(level, arg) : arg)));
|
||||
console[level](...args.flatMap(arg => (typeof arg === 'string' ? fmt(level, arg) : arg)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ function fmt(level: Level, text: string): string[] {
|
||||
function colorize(text: string): string[] {
|
||||
let args: string[] = [];
|
||||
|
||||
text = text.replace(/\[\w+\]/g, (block) => {
|
||||
text = text.replace(/\[\w+\]/g, block => {
|
||||
const code = block.replace('[', '').replace(']', '');
|
||||
const color = (colors as Obj)[code];
|
||||
args.push(`color: ${color};`);
|
||||
|
||||
@@ -10,7 +10,7 @@ export * from '$lib/mcp.d';
|
||||
// can send to the LLM.
|
||||
//
|
||||
export async function getMCPTools(session: ISession): Promise<Tool[]> {
|
||||
return (await invoke<McpTool[]>('get_mcp_tools', { sessionId: session.id })).map((tool) => {
|
||||
return (await invoke<McpTool[]>('get_mcp_tools', { sessionId: session.id })).map(tool => {
|
||||
return {
|
||||
type: 'function',
|
||||
function: {
|
||||
|
||||
@@ -57,13 +57,13 @@ export default class App extends Model<IApp, Row>('apps') {
|
||||
};
|
||||
|
||||
static hasContext(app: IApp): boolean {
|
||||
return app.nodes?.find((n) => n.type == NodeType.Context) !== undefined;
|
||||
return app.nodes?.find(n => n.type == NodeType.Context) !== undefined;
|
||||
}
|
||||
|
||||
static context(app: IApp): string {
|
||||
return app.nodes
|
||||
.filter((n) => n.type == NodeType.Context)
|
||||
.map((n) => n.config.value)
|
||||
.filter(n => n.type == NodeType.Context)
|
||||
.map(n => n.config.value)
|
||||
.join('\n\n');
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default class App extends Model<IApp, Row>('apps') {
|
||||
}
|
||||
|
||||
static removeNode(app: IApp, node: Node): IApp {
|
||||
app.nodes = app.nodes.filter((n) => n.uuid !== node.uuid);
|
||||
app.nodes = app.nodes.filter(n => n.uuid !== node.uuid);
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ export default class App extends Model<IApp, Row>('apps') {
|
||||
]);
|
||||
|
||||
if (result.rowsAffected == 1) {
|
||||
app.mcpServers = app.mcpServers.filter((m) => m.id == mcpServer.id);
|
||||
app.mcpServers = app.mcpServers.filter(m => m.id == mcpServer.id);
|
||||
return app.mcpServers;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
static async sync(): Promise<void> {
|
||||
repo = [];
|
||||
|
||||
(await this.query(`SELECT * FROM ${table}`)).forEach((record) => this.syncOne(record));
|
||||
(await this.query(`SELECT * FROM ${table}`)).forEach(record => this.syncOne(record));
|
||||
|
||||
info(`[green]✔ synced ${table}`);
|
||||
}
|
||||
@@ -175,7 +175,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
* Find an individual record by`id`.
|
||||
*/
|
||||
static find(id: number | string): Interface {
|
||||
return this.all().find((m) => m.id == Number(id)) as Interface;
|
||||
return this.all().find(m => m.id == Number(id)) as Interface;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,7 +189,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
* Find a collection of records by a set of the model's properties.
|
||||
*/
|
||||
static where(params: Partial<Interface>): Interface[] {
|
||||
return repo.filter((m) => {
|
||||
return repo.filter(m => {
|
||||
return Object.entries(params).every(([key, value]) => m[key] == value);
|
||||
});
|
||||
}
|
||||
@@ -329,7 +329,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
).rowsAffected >= 1;
|
||||
|
||||
if (success) {
|
||||
instances.forEach((instance) => {
|
||||
instances.forEach(instance => {
|
||||
this.syncRemove(instance);
|
||||
});
|
||||
}
|
||||
@@ -343,7 +343,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
protected static async query(sql: string, values: unknown[] = []): Promise<Interface[]> {
|
||||
const result: Row[] = await (await this.db()).select<Row[]>(sql, values);
|
||||
|
||||
return await Promise.all(result.map(async (row) => await this.fromSql(row)));
|
||||
return await Promise.all(result.map(async row => await this.fromSql(row)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -374,7 +374,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
* Remove an instance from the repo
|
||||
*/
|
||||
private static syncRemove(instance: Interface) {
|
||||
repo = repo.filter((i) => i.id !== instance.id);
|
||||
repo = repo.filter(i => i.id !== instance.id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,7 +389,7 @@ export default function Model<Interface extends Obj, Row extends Obj>(table: str
|
||||
* This leaves only persisted records(ones with an`id`).
|
||||
*/
|
||||
private static removeEphemeralInstances() {
|
||||
repo = repo.filter((record) => record.id !== undefined);
|
||||
repo = repo.filter(record => record.id !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -490,7 +490,7 @@ export function BareModel<T extends Obj>() {
|
||||
}
|
||||
|
||||
static delete(instance: T) {
|
||||
repo = repo.filter((i) => i !== instance);
|
||||
repo = repo.filter(i => i !== instance);
|
||||
}
|
||||
|
||||
static all(): T[] {
|
||||
|
||||
@@ -14,11 +14,7 @@ interface Row {
|
||||
value: string;
|
||||
}
|
||||
|
||||
type ConfigKey =
|
||||
| 'latest-session-id'
|
||||
| 'welcome-agreed'
|
||||
| 'skipped-version'
|
||||
| 'default-model';
|
||||
type ConfigKey = 'latest-session-id' | 'welcome-agreed' | 'skipped-version' | 'default-model';
|
||||
|
||||
export default class Config extends Model<IConfig, Row>('config') {
|
||||
@getset('latest-session-id')
|
||||
@@ -81,7 +77,7 @@ export default class Config extends Model<IConfig, Row>('config') {
|
||||
}
|
||||
|
||||
function getset(key: ConfigKey) {
|
||||
return function(target: object, property: string) {
|
||||
return function (target: object, property: string) {
|
||||
function get() {
|
||||
return Config.get(key);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,6 @@ export default class Engine extends BareModel<IEngine>() {
|
||||
});
|
||||
}
|
||||
|
||||
Model.reset(this.all().flatMap((engine) => engine.models));
|
||||
Model.reset(this.all().flatMap(engine => engine.models));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ export function from(message: IMessage): Message {
|
||||
return {
|
||||
role: message.role,
|
||||
content: message.content,
|
||||
tool_calls: message.toolCalls?.map((c) => ({
|
||||
tool_calls: message.toolCalls?.map(c => ({
|
||||
function: {
|
||||
name: c.function.name,
|
||||
arguments: c.function.arguments,
|
||||
|
||||
@@ -53,7 +53,7 @@ function toolCalls(message: IMessage): OpenAI.ChatCompletionMessageToolCall[] |
|
||||
return;
|
||||
}
|
||||
|
||||
return message.toolCalls.map((call) => ({
|
||||
return message.toolCalls.map(call => ({
|
||||
id: call.id as string,
|
||||
type: 'function',
|
||||
function: {
|
||||
|
||||
@@ -86,7 +86,7 @@ export default class Session extends Base<ISession, Row>('sessions') {
|
||||
|
||||
static async removeMcpServer(session: ISession, server: IMcpServer): Promise<ISession> {
|
||||
session.config.enabledMcpServers = session.config.enabledMcpServers.filter(
|
||||
(s) => s !== server.name
|
||||
s => s !== server.name
|
||||
);
|
||||
return await this.update(session);
|
||||
}
|
||||
|
||||
@@ -4,13 +4,13 @@ import Message from '$lib/models/message';
|
||||
|
||||
export async function migrate() {
|
||||
await Promise.all(
|
||||
Message.all().map(async (message) => {
|
||||
Message.all().map(async message => {
|
||||
if (message.toolCalls.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
message.toolCalls.map(async (tc) => {
|
||||
message.toolCalls.map(async tc => {
|
||||
// Ollama tool calls didn't always have an `id`. Add one
|
||||
// now if that's the case.
|
||||
if (!tc.id) {
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
onNavigate((navigation) => {
|
||||
onNavigate(navigation => {
|
||||
if (!document.startViewTransition) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
document.startViewTransition(async () => {
|
||||
resolve();
|
||||
await navigation.complete;
|
||||
|
||||
@@ -7,11 +7,7 @@
|
||||
import Svg from '$components/Svg.svelte';
|
||||
import Updater from '$components/Updater.svelte';
|
||||
import Welcome from '$components/Welcome.svelte';
|
||||
import startup, {
|
||||
type Condition,
|
||||
type OnSuccess,
|
||||
StartupCheck,
|
||||
} from '$lib/startup';
|
||||
import startup, { type Condition, type OnSuccess, StartupCheck } from '$lib/startup';
|
||||
|
||||
const messages = {
|
||||
[StartupCheck.NoModels]: 'No Engines Connected',
|
||||
@@ -73,9 +69,6 @@
|
||||
{/if}
|
||||
</Modal>
|
||||
{:else}
|
||||
<Svg
|
||||
name="Logo"
|
||||
class="text-dark fixed top-[50%] left-[50%] h-32 w-32 -translate-[50%]"
|
||||
/>
|
||||
<Svg name="Logo" class="text-dark fixed top-[50%] left-[50%] h-32 w-32 -translate-[50%]" />
|
||||
{/if}
|
||||
</Layout>
|
||||
|
||||
@@ -20,14 +20,12 @@
|
||||
import Session, { type ISession } from '$lib/models/session';
|
||||
|
||||
const session: ISession = $derived(Session.find(page.params.session_id));
|
||||
const model: IModel | undefined = $derived(
|
||||
Model.find(session.config.model)
|
||||
);
|
||||
const model: IModel | undefined = $derived(Model.find(session.config.model));
|
||||
|
||||
const sessions: ISession[] = $derived(Session.all());
|
||||
const mcpServers: IMcpServer[] = $derived(McpServer.all());
|
||||
const engines: IEngine[] = $derived(Engine.all());
|
||||
const hasModels = $derived(engines.flatMap((e) => e.models).length > 0);
|
||||
const hasModels = $derived(engines.flatMap(e => e.models).length > 0);
|
||||
|
||||
let advancedIsOpen = $state(false);
|
||||
|
||||
@@ -37,8 +35,8 @@
|
||||
}
|
||||
|
||||
async function startMcpServers(session: ISession) {
|
||||
session.config.enabledMcpServers.forEach(async (name) => {
|
||||
const server = mcpServers.find((s) => s.name == name);
|
||||
session.config.enabledMcpServers.forEach(async name => {
|
||||
const server = mcpServers.find(s => s.name == name);
|
||||
|
||||
if (server) {
|
||||
await startMcpServer(server);
|
||||
@@ -102,9 +100,7 @@
|
||||
</script>
|
||||
|
||||
{#snippet titlebar()}
|
||||
<Flex
|
||||
class="border-r-light z-10 h-full w-[300px] items-center border-r px-8 pr-4"
|
||||
>
|
||||
<Flex class="border-r-light z-10 h-full w-[300px] items-center border-r px-8 pr-4">
|
||||
<h1 class="grow font-[500]">Chat</h1>
|
||||
<button
|
||||
onclick={() => addSession()}
|
||||
@@ -118,18 +114,14 @@
|
||||
|
||||
<Layout {titlebar}>
|
||||
<Flex class="h-full items-start">
|
||||
<Flex
|
||||
class="border-light bg-medium h-content w-[300px] flex-col overflow-auto border-r"
|
||||
>
|
||||
<Flex class="border-light bg-medium h-content w-[300px] flex-col overflow-auto border-r">
|
||||
{#each sessions as sess (sess.id)}
|
||||
<Flex
|
||||
class={`text-medium border-b-light w-full justify-between border-b
|
||||
border-l-transparent text-sm ${sess.id == session?.id ? '!border-l-purple border-l' : ''}`}
|
||||
>
|
||||
<Menu items={menuItems(sess)}>
|
||||
<Deleteable
|
||||
ondelete={async () => await deleteSession(sess)}
|
||||
>
|
||||
<Deleteable ondelete={async () => await deleteSession(sess)}>
|
||||
<Link
|
||||
href={`/chat/${sess.id}`}
|
||||
class="w-full py-3 pl-8 text-left"
|
||||
@@ -145,17 +137,13 @@
|
||||
</Flex>
|
||||
|
||||
{#if session}
|
||||
<Flex
|
||||
class="bg-medium h-full w-[calc(100%-600px)] grow items-start"
|
||||
>
|
||||
<Flex class="bg-medium h-full w-[calc(100%-600px)] grow items-start">
|
||||
{#key session.id}
|
||||
<Chat {session} bind:model={session.config.model} />
|
||||
{/key}
|
||||
</Flex>
|
||||
|
||||
<Flex
|
||||
class="bg-medium border-light h-full w-[300px] flex-col items-start border-l p-4"
|
||||
>
|
||||
<Flex class="bg-medium border-light h-full w-[300px] flex-col items-start border-l p-4">
|
||||
{#key session.config.model}
|
||||
<ModelMenu
|
||||
{engines}
|
||||
@@ -190,10 +178,8 @@
|
||||
<Flex class="text-light z-0 mb-4 ml-2">
|
||||
<Toggle
|
||||
label={server.name}
|
||||
value={Session.hasMcpServer(
|
||||
session,
|
||||
server.name
|
||||
) && model?.supportsTools
|
||||
value={Session.hasMcpServer(session, server.name) &&
|
||||
model?.supportsTools
|
||||
? 'on'
|
||||
: 'off'}
|
||||
disabled={!model?.supportsTools}
|
||||
@@ -209,19 +195,14 @@
|
||||
class="text-dark mb-4 ml-2 self-start text-sm font-medium hover:cursor-pointer"
|
||||
onclick={() => toggleAdvanced()}
|
||||
>
|
||||
Advanced <span class="ml-4"
|
||||
>{advancedIsOpen ? '⏷' : '⏵'}</span
|
||||
>
|
||||
Advanced <span class="ml-4">
|
||||
{advancedIsOpen ? '⏷' : '⏵'}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{#if advancedIsOpen}
|
||||
<Flex
|
||||
class="m-auto w-full flex-col items-start px-4 pt-4 pl-0"
|
||||
>
|
||||
<label
|
||||
for="ctx_num"
|
||||
class="text-medium mb-1 ml-2 text-sm"
|
||||
>
|
||||
<Flex class="m-auto w-full flex-col items-start px-4 pt-4 pl-0">
|
||||
<label for="ctx_num" class="text-medium mb-1 ml-2 text-sm">
|
||||
Context Window Size
|
||||
</label>
|
||||
<input
|
||||
@@ -234,10 +215,7 @@
|
||||
bind:value={session.config.contextWindow}
|
||||
/>
|
||||
|
||||
<label
|
||||
for="temperature"
|
||||
class="text-medium mt-4 mb-1 ml-2 text-sm"
|
||||
>
|
||||
<label for="temperature" class="text-medium mt-4 mb-1 ml-2 text-sm">
|
||||
Temperature
|
||||
</label>
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@
|
||||
{#if config.type !== 'stdio'}
|
||||
<Flex class="text-red w-full">
|
||||
<Svg name="Warning" class="mr-8 h-6 w-6" />
|
||||
<p>Tome only supports <code>stdio</code> MCP servers</p>
|
||||
<p>
|
||||
Tome only supports <code>stdio</code>
|
||||
MCP servers
|
||||
</p>
|
||||
</Flex>
|
||||
{:else}
|
||||
<Flex class="w-full flex-col items-start">
|
||||
|
||||
Reference in New Issue
Block a user