Skip to content

Commit

Permalink
Fix folder rename input (#1098)
Browse files Browse the repository at this point in the history
- fix persona squish/unsquish
- fix change folder entry
- prevent duplicate subscriptions
- do not add char names from trim if not in end tokens
- model categories by tier
  • Loading branch information
sceuick authored Jan 1, 2025
1 parent 532dd0a commit 9f94eef
Show file tree
Hide file tree
Showing 19 changed files with 272 additions and 46 deletions.
15 changes: 8 additions & 7 deletions common/requests/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,14 @@ export function trimResponseV2(
generated = generated.split(`${member.handle} :`).join(`${member.handle}:`)
}

if (bots) {
for (const bot of Object.values(bots)) {
if (!bot) continue
if (bot?._id === char._id) continue
endTokens.push(`${bot.name}:`)
}
}
/** Do not always add character names as stop tokens here */
// if (bots) {
// for (const bot of Object.values(bots)) {
// if (!bot) continue
// if (bot?._id === char._id) continue
// endTokens.push(`${bot.name}:`)
// }
// }

let index = -1
let trimmed = allEndTokens.concat(...endTokens).reduce((prev, endToken) => {
Expand Down
3 changes: 2 additions & 1 deletion common/types/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export type CustomUI = {
chatQuoteColor: string
}

export type MessageOption = 'edit' | 'regen' | 'trash' | 'fork' | 'prompt'
export type MessageOption = 'edit' | 'regen' | 'trash' | 'fork' | 'prompt' | 'schema-regen'

export type UISettings = {
theme: string
Expand Down Expand Up @@ -147,5 +147,6 @@ export const defaultUIsettings: UISettings = {
fork: { outer: false, pos: 2 },
regen: { outer: true, pos: 1 },
trash: { outer: false, pos: 4 },
'schema-regen': { outer: false, pos: 5 },
},
}
12 changes: 9 additions & 3 deletions srv/adapter/agnaistic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,15 @@ export const handleAgnaistic: ModelAdapter = async function* (opts) {

log.debug(`Prompt:\n${prompt}`)

const [submodel, override] = subPreset.subModel.split(',')

let params = [`type=text`, `id=${opts.user._id}`, `model=${submodel}`, `level=${level}`]
const [submodel, override = ''] = subPreset.subModel.split(',')

let params = [
`type=text`,
`id=${opts.user._id}`,
`model=${submodel}`,
`level=${level}`,
`sub_model=${override}`,
]
.filter((p) => !!p)
.join('&')

Expand Down
1 change: 1 addition & 0 deletions srv/adapter/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ export async function createChatStream(
lastMessage: opts.lastMessage,
imageData: opts.imageData,
jsonSchema: jsonSchema || opts.jsonSchema,
reschemaPrompt: opts.reschemaPrompt,
subscription,
encoder,
jsonValues: opts.jsonValues,
Expand Down
1 change: 1 addition & 0 deletions srv/adapter/payloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ function getBasePayload(opts: AdapterProps, stops: string[] = []) {
lists: opts.lists,
previous: opts.previous,
json_schema_v2: ensureSafeSchema(json_schema),
reschema_prompt: opts.reschemaPrompt,
json_schema,
imageData: opts.imageData,
context_size: opts.contextSize,
Expand Down
2 changes: 2 additions & 0 deletions srv/adapter/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export type GenerateRequestV2 = {
impersonate?: AppSchema.Character

jsonSchema?: JsonField[]
reschemaPrompt?: string
jsonValues?: Record<string, any>

/** Base64 */
Expand Down Expand Up @@ -122,6 +123,7 @@ export type AdapterProps = {
encoder?: TokenCounter

jsonSchema?: any
reschemaPrompt?: string
jsonValues: Record<string, any> | undefined

imageData?: string
Expand Down
24 changes: 24 additions & 0 deletions srv/api/billing/checkout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { v4 } from 'uuid'
import { config } from '../../config'
import { billingCmd, domain } from '../../domains'
import { subsCmd } from '../../domains/subs/cmd'
import { AppSchema } from '/common/types'

export const startCheckout = handle(async ({ body, userId }) => {
assertValid({ tierId: 'string', callback: 'string' }, body)
Expand Down Expand Up @@ -180,6 +181,10 @@ export const finishCheckout = handle(async ({ body, userId }) => {
subscriptionId: subscription.id,
})

if (user && subscription.id) {
await ensureOnlyActiveSubscription(user, subscription.id)
}

const config = await store.users.updateUser(userId, {
sub: {
tierId: agg.tierId,
Expand All @@ -205,3 +210,22 @@ export const finishCheckout = handle(async ({ body, userId }) => {
await billingCmd.cancel(body.sessionId, { userId })
}
})

async function ensureOnlyActiveSubscription(user: AppSchema.User, subscriptionId: string) {
// The user isn't an existing customer -- ignore
if (!user.billing?.customerId) return

const subs = await stripe.subscriptions
.list({ customer: user.billing.customerId, status: 'active' })
.then((res) => res.data)
.catch(() => [])

for (const sub of subs) {
if (sub.id !== subscriptionId) {
await subsCmd.cancelDuplicate(user._id, { subscriptionId, replacementId: subscriptionId })
await stripe.subscriptions.cancel(sub.id, {
cancellation_details: { comment: 'duplicate detected during checkout' },
})
}
}
}
8 changes: 8 additions & 0 deletions srv/domains/subs/agg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ export const subsAgg = createAggregate<SubsEvt, SubsAgg, 'subscriptions'>({
}
}

case 'cancelled-duplicate': {
return {
state: prev.state,
downgrade: prev.downgrade,
history,
}
}

case 'cancelled': {
const endAt = new Date(prev.periodStart)
endAt.setFullYear(meta.timestamp.getFullYear())
Expand Down
9 changes: 9 additions & 0 deletions srv/domains/subs/cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ export const subsCmd = createCommands<SubsEvt, SubsAgg, SubsCmd>(domain.subscrip

return { type: 'resumed' }
},

async cancelDuplicate(cmd, agg) {
return {
type: 'cancelled-duplicate',
subscriptionId: cmd.subscriptionId,
replacementId: cmd.replacementId,
}
},

async cancel(cmd, agg) {
if (agg.state !== 'active') {
throw new CommandError('Cannot cancel subscription - Subscription not active', 'NOT_ACTIVE')
Expand Down
2 changes: 2 additions & 0 deletions srv/domains/subs/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type SubsEvt =
periodStart: string
}
| { type: 'cancelled' }
| { type: 'cancelled-duplicate'; subscriptionId: string; replacementId?: string }
| { type: 'upgraded'; tierId: string; priceId: string }
| { type: 'downgraded'; tierId: string; priceId: string; activeAt: string }
| { type: 'session-started'; sessionId: string; tierId: string }
Expand All @@ -36,6 +37,7 @@ export type SubsCmd =
productId: string
subscription: Stripe.Subscription
}
| { type: 'cancelDuplicate'; subscriptionId: string; replacementId?: string }
| { type: 'cancel' }
| { type: 'resume' }
| { type: 'upgrade'; tierId: string; priceId: string }
Expand Down
12 changes: 6 additions & 6 deletions web/pages/Character/components/CharacterFolderView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -442,15 +442,15 @@ function getChildFolders(tree: FolderTree, path: string, sort: SortDirection) {
}

const ChangeFolder: Component<{ char?: AppSchema.Character; close: () => void }> = (props) => {
const [actual, setActual] = createSignal('/')
const [name, setName] = createSignal(props.char?.folder || '')
const actual = createMemo(() => toFolderSlug(name()))

createEffect(
on(
() => props.char,
() => {
if (!props.char) return

setActual(toFolderSlug(props.char.folder || '/'))
setName(props.char.folder || '/')
}
)
)
Expand Down Expand Up @@ -489,10 +489,10 @@ const ChangeFolder: Component<{ char?: AppSchema.Character; close: () => void }>
/>

<TextInput
helperMarkdown={`Folder names are 'normalized'.\nNoramlized name: ${actual()}`}
helperMarkdown={`Folder names are 'normalized'.\nNormalized name: ${actual()}`}
label="New Folder"
value={props.char?.folder || '/'}
onChange={(ev) => setActual(toFolderSlug(ev.currentTarget.value))}
value={name()}
onChange={(ev) => setName(ev.currentTarget.value)}
/>
</div>
</RootModal>
Expand Down
33 changes: 28 additions & 5 deletions web/pages/Chat/components/Message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
Zap,
Split,
MoreHorizontal,
Braces,
} from 'lucide-solid'
import {
Accessor,
Expand Down Expand Up @@ -548,10 +549,6 @@ const MessageOptions: Component<{
onRemove: () => void
showMore: Signal<boolean>
}> = (props) => {
const showInner = createMemo(() =>
Object.values(props.ui.msgOptsInline || {}).some((v) => !v?.outer)
)

const closer = (action: () => void) => {
return () => {
action()
Expand Down Expand Up @@ -617,6 +614,20 @@ const MessageOptions: Component<{
icon: RefreshCw,
},

'schema-regen': {
key: 'schema-regen',
class: 'refresh-btn',
label: 'Schema Regen',
outer: props.ui.msgOptsInline['schema-regen'],
show:
window.flags.reschema &&
((props.msg.json && props.last) ||
(props.msg.adapter === 'image' && !!props.msg.imagePrompt)) &&
!!props.msg.characterId,
onClick: () => !props.partial && retryJsonSchema(props.msg, props.msg),
icon: Braces,
},

trash: {
key: 'trash',
label: 'Delete',
Expand All @@ -632,6 +643,14 @@ const MessageOptions: Component<{
return items
})

const showInner = createMemo(() => {
for (const opt of Object.values(logic())) {
if (!opt.outer && opt.show) return true
}

return false
})

const order = createMemo(() => {
open()
logic()
Expand Down Expand Up @@ -736,7 +755,7 @@ const MessageOption: Component<{

<Show when={!props.outer}>
<Button
class={`${props.class || ''} w-full`}
class={`${props.class || ''} w-full min-w-max`}
schema={props.schema || 'secondary'}
onClick={props.onClick}
size="sm"
Expand All @@ -758,6 +777,10 @@ function retryMessage(original: AppSchema.ChatMessage, split: SplitMessage) {
}
}

function retryJsonSchema(original: AppSchema.ChatMessage, split: SplitMessage) {
msgStore.retrySchema(split.chatId, original._id)
}

function renderMessage(ctx: ContextState, text: string, isUser: boolean, adapter?: string) {
// Address unfortunate Showdown bug where spaces in code blocks are replaced with nbsp, except
// it also encodes the ampersand, which results in them actually being rendered as `&amp;nbsp;`
Expand Down
1 change: 1 addition & 0 deletions web/pages/Settings/UISettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const msgInlineLabels: Record<UI.MessageOption, string> = {
prompt: 'Prompt View',
fork: 'Fork',
trash: 'Delete',
'schema-regen': 'Retry Schema',
}

const UISettings: Component<{}> = () => {
Expand Down
39 changes: 29 additions & 10 deletions web/shared/CustomSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export type CustomOption = {
export const CustomSelect: Component<{
buttonLabel: string | JSX.Element | ((opt: CustomOption) => JSX.Element | string)
onSelect: (opt: CustomOption) => void
options: CustomOption[]
options?: CustomOption[]
categories?: Array<{ name: string; options: CustomOption[] }>
value: any

header?: JSX.Element
Expand Down Expand Up @@ -74,15 +75,33 @@ export const CustomSelect: Component<{
</div>
<RootModal show={open()} close={() => setOpen(false)} title={props.modalTitle}>
<div class="flex flex-col gap-4">
<div class="flex flex-wrap gap-2 pr-3">
<OptionList
header={props.header}
options={props.options}
onSelect={onSelect}
selected={props.selected}
search={props.search}
/>
</div>
<Show when={props.categories}>
<For each={props.categories}>
{(category) => (
<div class="flex flex-wrap gap-2 pr-3">
<div class="bold text-md">{category.name}</div>
<OptionList
header={props.header}
options={category.options}
onSelect={onSelect}
selected={props.selected}
search={props.search}
/>
</div>
)}
</For>
</Show>
<Show when={props.options}>
<div class="flex flex-wrap gap-2 pr-3">
<OptionList
header={props.header}
options={props.options!}
onSelect={onSelect}
selected={props.selected}
search={props.search}
/>
</div>
</Show>
</div>
</RootModal>
</div>
Expand Down
6 changes: 6 additions & 0 deletions web/shared/PersonaAttributes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ const PersonaAttributes: Component<{
if (key === 'text') {
squished.push(values)
} else {
if (!values.trim()) continue
squished.push(`${key}:\n${values}`)
}
}
Expand All @@ -82,6 +83,11 @@ const PersonaAttributes: Component<{
const text = props.state.find((s) => s.key === 'text')
if (!text) return

if (props.state.length === 1) {
props.setter([{ key: 'personality', values: props.state[0].values }])
return
}

let matching = true
for (const { values } of props.state) {
if (!text.values.includes(values)) matching = false
Expand Down
Loading

0 comments on commit 9f94eef

Please sign in to comment.