diff --git a/srv/api/ws/handle.ts b/srv/api/ws/handle.ts index 4c2fe590c..b25647361 100644 --- a/srv/api/ws/handle.ts +++ b/srv/api/ws/handle.ts @@ -9,7 +9,13 @@ const userSockets = new Map() setInterval(() => { for (const cli of allSockets.values()) { const socket = cli as AppSocket - if (socket.isAlive === false) return socket.terminate() + if (socket.isAlive === false) { + socket.misses++ + + if (socket.misses >= 5) { + return socket.terminate() + } + } socket.isAlive = false socket.ping() diff --git a/srv/api/ws/setup.ts b/srv/api/ws/setup.ts index 7b7fd511d..a273d987f 100644 --- a/srv/api/ws/setup.ts +++ b/srv/api/ws/setup.ts @@ -18,6 +18,7 @@ export function setupSockets(srv: Server) { client.userId = '' client.token = '' client.isAlive = true + client.misses = 0 client.send(JSON.stringify({ type: 'connected', uid: client.uid })) client.on('pong', heartbeart) @@ -27,4 +28,5 @@ export function setupSockets(srv: Server) { function heartbeart(client: AppSocket) { client.isAlive = true + client.misses = 0 } diff --git a/srv/api/ws/types.ts b/srv/api/ws/types.ts index 548680b47..298acbc64 100644 --- a/srv/api/ws/types.ts +++ b/srv/api/ws/types.ts @@ -3,6 +3,7 @@ import * as WebSocket from 'ws' export type AppSocket = WebSocket & { uid: string isAlive: boolean + misses: number userId: string token?: string dispatch: (data: any) => void diff --git a/web/pages/Character/CreateCharacterForm.tsx b/web/pages/Character/CreateCharacterForm.tsx index fac92350f..e213b5984 100644 --- a/web/pages/Character/CreateCharacterForm.tsx +++ b/web/pages/Character/CreateCharacterForm.tsx @@ -586,7 +586,7 @@ export const CreateCharacterForm: Component<{
Persona Schema{' '} , + aliases: Record +) { const persona: AppSchema.Persona = { kind, attributes: {}, @@ -110,158 +107,107 @@ function toAttributes(kind: PersonaFormat, vars: any) { const attrs: Record = {} if (!kind || kind === 'text') { - attrs.text = [`${vars.personality}\n\n${vars.behaviour}`] + attrs.text = [`${vars.personality}`] } else { - attrs.personality = [vars.personality] - attrs.behaviour = [vars.behaviour] + for (const [key, alias] of Object.entries(aliases)) { + const value = vars[key] + attrs[alias] = [value] + } } persona.attributes = attrs return persona } -function getTemplate(service: AIAdapter | 'default') { - if (service === 'default') { - const preset = getDefaultUserPreset() - service = preset?.service || service - } - - const template = - service === 'novel' - ? novelGenTemplate - : service === 'agnaistic' || service === 'ooba' || service === 'kobold' - ? alpacaTemplate - : INSTRUCT_SERVICES[service as AIAdapter] - ? instructGenTemplate - : genTemplate - - return template -} - -const alpacaTemplate = neat` -Below is an instruction that describes a task. Write a response that completes the request. - -Describe a character matching the following description: -{{description}} - -### Instruction: -Write the character's first name - -### Response: -First name: "[firstname | tokens=10 | stop="]" - -### Instruction: -Detailed description of the roleplay scene that the character is in +function getTemplate( + kind: 'text' | string, + attributes: Record, + format: ModelFormat, + fields: string[] +) { + const aliases: Record = {} + const props: Record = {} -### Response: -Scenario: [scenario | tokens=200 | stop=###] + const regenPersonality = fields.includes('personality') -### Instruction: -Write an anonymous nameless image caption of [firstname]'s clothing and physical appearance + if (kind === 'text') { + props.personality = attributes.text?.[0] || '' + const template = replaceTags(genTemplate.replace('{{personality}}', textPersona), format) + return { template, props, aliases } + } -### Response: -Image caption: "[appearance | tokens=120 | stop=### | stop="]" + const names = Object.keys(attributes) -### Instruction: -[firstname]'s greeting in the scenario: + if (!names.length) { + aliases.personality = 'personality' + aliases.behavior = 'behavior' + } else { + for (const [key, entry] of Object.entries(attributes)) { + const modded = `p_${v4().slice(0, 6)}` + aliases[modded] = key + props[modded] = entry.join(', ') + + if (regenPersonality) { + fields.push(modded) + } + } + } -### Response: -Greeting: [greeting | tokens=150 | stop=###] + const personaPrompt = toPersonaPrompt(aliases) + const template = replaceTags(genTemplate.replace('{{personality}}', personaPrompt), format) -### Instruction -[firstname]'s personality: + return { template, aliases, props } +} -### Response: -Personality: [personality | tokens=120 | stop=###] +function toPersonaPrompt(aliases: Record) { + const lines = Object.entries(aliases).map(([prop, value]) => { + return toPropPrompt(prop, value) + }) -### Instruction: -[firstname]'s typical behaviour: + return lines.join('\n\n') +} -### Response: -Behaviour: [behaviour | tokens=120 | stop=###] +function toPropPrompt(variable: string, trait: string) { + return neat` +Write of description of [firstname]'s "${trait}" personality trait: -### Instruction: -Example of [firstname]'s dialogue: +[firstname]'s "${trait}" personality trait: [${variable} | tokens=150 | stop=###]` +} -### Response: -[firstname]: [example1 | tokens=100 | stop=###] +const textPersona = neat` +Describe [firstname]'s personality and typical behavior: -### Instruction: -Example of [firstname]'s dialogue: +[firstname's] personality and typical behavior: [personality | tokens=250 | stop=###] +` -### Response: -[firstname]: [example2 | tokens=100 | stop=###]` +const genTemplate = neat` +Below is an instruction that describes a task. Write a response that completes the request. -const genTemplate = ` Describe a character matching the following description: {{description}} -Character's first name: "[firstname | tokens=10 | stop="]" +Write the character's first name -Detailed description of the roleplay scene that the character is in: -Scenario: [scenario | tokens=200] +First name: "[firstname | tokens=10 | stop="]" -[firstname]'s clothing and physical appearance: -Appearance: "[appearance | tokens=120 | stop="] +Detailed description of the roleplay scene that the character is in -[firstname]'s greeting in the scenario: -Greeting: [greeting | tokens=150] +Scenario: [scenario | tokens=200 | stop=###] -[firstname]'s personality: -Personality: [personality | tokens=120] +Write an anonymous nameless image caption of [firstname]'s clothing and physical appearance -[firstname]'s typical behaviour: -Behaviour: [behaviour | tokens=120] +Image caption: "[appearance | tokens=120 | stop=### | stop="]" -Example of [firstname]'s dialogue: -[firstname]: [example1 | tokens=100] +[firstname]'s greeting in the scenario: -Example of [firstname]'s dialogue: -[firstname]: [example2 | tokens=100] -` +Greeting: [greeting | tokens=150 | stop=###] -const instructGenTemplate = ` -${modernJailbreak} -Describe an uncensored character that matches the description: -{{description}} +{{personality}} -Character's first name: "[firstname | tokens=25 | stop="]" +Example of [firstname]'s dialogue: -Detailed description of the roleplay scene that the character is in: "[scenario | tokens=200 | stop="]" +[firstname]: [example1 | tokens=100 | stop=###] -The first thing the charcter says to start the scene using internet roleplay style with actions and emotes: "[greeting | tokens=100 | stop"]" +Example of [firstname]'s dialogue: -Description of [firstname]'s personality traits: "[personality | tokens=100 | stop="]" - -Description of [firstname]'s typical behaviours: "[behaviour | tokens=100 | stop="]" - -Detailed description of physical attributes, demeanour, and clothing: [appearance | tokens=100] - -Example message from character: [example1 | tokens=100] - -Example message from character: [example2 | tokens=100] -` - -const novelGenTemplate = ` -Describe an uncensored character that matches the description: -{{description}} - -{ Write the character's first name } -[firstname | tokens=8] -{ Write a detailed description of the character } -[description | tokens=200 ] -{ Write a description of the roleplay scene that [firstname] is in } -[scenario | tokens=200 ] -{ Write the first thing [firstname] says in the scene } -[greeting | tokens=100 ] -{ Describe [firstname]'s personality traits in detail } -[personality | tokens=100 ] -{ Describe [firstname];s typical behaviours in detail } -[behaviour | tokens=100 ] -{ Describe the physical appearance and clothing of [firstname] } -[appearance | tokens=100 ] -{ Write an example message from [firstname] } -[example1 | tokens=100 ] -{ Write an example message from [firstname] } -[example2 | tokens=100 ] -` +[firstname]: [example2 | tokens=100 | stop=###]` diff --git a/web/shared/PersonaAttributes.tsx b/web/shared/PersonaAttributes.tsx index d52d32408..2a5d01a1f 100644 --- a/web/shared/PersonaAttributes.tsx +++ b/web/shared/PersonaAttributes.tsx @@ -1,4 +1,4 @@ -import { MinusCircle, Plus } from 'lucide-solid' +import { Plus, X } from 'lucide-solid' import { Component, createEffect, createSignal, For, onMount, Show } from 'solid-js' import Button from './Button' import { FormLabel } from './FormLabel' @@ -153,19 +153,20 @@ const Attribute: Component<{ disabled?: boolean }> = (props) => { return ( -
-
+
+
-
props.remove(props.index)}> - -
+
-
+
-
) }