Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions convex/crons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ crons.interval(
{ batchSize: 200, maxBatches: 5 },
)

crons.interval(
'skill-stat-events',
{ minutes: 5 },
internal.skillStatEvents.processSkillStatEventsInternal,
{ batchSize: 100 },
)
// Commented out: replaced by action-based processSkillStatEventsAction
// which should be triggered manually or via a different scheduling mechanism
// crons.interval(
// 'skill-stat-events',
// { minutes: 5 },
// internal.skillStatEvents.processSkillStatEventsInternal,
// { batchSize: 100 },
// )

export default crons
2 changes: 1 addition & 1 deletion convex/devSeed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { internal } from './_generated/api'
import type { ActionCtx } from './_generated/server'
import { internalAction, internalMutation } from './_generated/server'
import { EMBEDDING_DIMENSIONS } from './lib/embeddings'
import { parseMoltbotMetadata, parseFrontmatter } from './lib/skills'
import { parseFrontmatter, parseMoltbotMetadata } from './lib/skills'

type SeedSkillSpec = {
slug: string
Expand Down
2 changes: 1 addition & 1 deletion convex/devSeedExtra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { internal } from './_generated/api'
import type { Id } from './_generated/dataModel'
import type { ActionCtx } from './_generated/server'
import { internalAction, internalMutation } from './_generated/server'
import { parseMoltbotMetadata, parseFrontmatter } from './lib/skills'
import { parseFrontmatter, parseMoltbotMetadata } from './lib/skills'

type SeedSkillSpec = {
slug: string
Expand Down
2 changes: 1 addition & 1 deletion convex/http.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiRoutes, LegacyApiRoutes } from 'molthub-schema'
import { httpRouter } from 'convex/server'
import { ApiRoutes, LegacyApiRoutes } from 'molthub-schema'
import { auth } from './auth'
import { downloadZip } from './downloads'
import {
Expand Down
4 changes: 1 addition & 3 deletions convex/lib/badges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ import type { QueryCtx } from '../_generated/server'

type BadgeKind = Doc<'skillBadges'>['kind']

export type SkillBadgeMap = Partial<
Record<BadgeKind, { byUserId: Id<'users'>; at: number }>
>
export type SkillBadgeMap = Partial<Record<BadgeKind, { byUserId: Id<'users'>; at: number }>>

export type SkillBadgeSource = { badges?: SkillBadgeMap | null }

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/githubImport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'
import { zipSync } from 'fflate'
import { TEXT_FILE_EXTENSION_SET } from 'molthub-schema'
import semver from 'semver'
import { parseFrontmatter } from './skills'

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skillBackfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {
getFrontmatterMetadata,
getFrontmatterValue,
type ParsedSkillFrontmatter,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
} from './skills'

export type ParsedSkillData = {
Expand Down
10 changes: 5 additions & 5 deletions convex/lib/skillPublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import semver from 'semver'
import { api, internal } from '../_generated/api'
import type { Doc, Id } from '../_generated/dataModel'
import type { ActionCtx, MutationCtx } from '../_generated/server'
import { getSkillBadgeMap, isSkillHighlighted } from './badges'
import { generateChangelogForPublish } from './changelog'
import { generateEmbedding } from './embeddings'
import { getSkillBadgeMap, isSkillHighlighted } from './badges'
import type { PublicUser } from './public'
import {
buildEmbeddingText,
getFrontmatterMetadata,
hashSkillFiles,
isTextFile,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
sanitizePath,
} from './skills'
import type { WebhookSkillPayload } from './webhooks'
Expand Down Expand Up @@ -166,9 +166,9 @@ export async function publishVersionForUser(
embedding,
})) as PublishResult

const owner = (await ctx.runQuery(internal.users.getByIdInternal, { userId })) as
| Doc<'users'>
| null
const owner = (await ctx.runQuery(internal.users.getByIdInternal, {
userId,
})) as Doc<'users'> | null
const ownerHandle = owner?.handle ?? owner?.displayName ?? owner?.name ?? 'unknown'

void ctx.scheduler
Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skills.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
getFrontmatterValue,
hashSkillFiles,
isTextFile,
parseMoltbotMetadata,
parseFrontmatter,
parseMoltbotMetadata,
sanitizePath,
} from './skills'

Expand Down
2 changes: 1 addition & 1 deletion convex/lib/skills.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {
isTextContentType,
type MoltbotConfigSpec,
type MoltbotSkillMetadata,
MoltbotSkillMetadataSchema,
isTextContentType,
type NixPluginSpec,
parseArk,
type SkillInstallSpec,
Expand Down
6 changes: 3 additions & 3 deletions convex/lib/soulPublish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ export async function publishSoulVersionForUser(
embedding,
})) as PublishResult

const owner = (await ctx.runQuery(internal.users.getByIdInternal, { userId })) as
| Doc<'users'>
| null
const owner = (await ctx.runQuery(internal.users.getByIdInternal, {
userId,
})) as Doc<'users'> | null
const ownerHandle = owner?.handle ?? owner?.name ?? userId

void ctx.scheduler
Expand Down
29 changes: 14 additions & 15 deletions convex/maintenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,9 +672,10 @@ export const backfillSkillBadges: ReturnType<typeof action> = action({
handler: async (ctx, args): Promise<BadgeBackfillActionResult> => {
const { user } = await requireUserFromAction(ctx)
assertRole(user, ['admin'])
return ctx.runAction(internal.maintenance.backfillSkillBadgesInternal, args) as Promise<
BadgeBackfillActionResult
>
return ctx.runAction(
internal.maintenance.backfillSkillBadgesInternal,
args,
) as Promise<BadgeBackfillActionResult>
},
})

Expand Down Expand Up @@ -771,15 +772,12 @@ export async function backfillSkillBadgeTableInternalHandler(
if (dryRun) continue

for (const entry of entries) {
const result = await ctx.runMutation(
internal.maintenance.upsertSkillBadgeRecordInternal,
{
skillId: item.skillId,
kind: entry.kind,
byUserId: entry.byUserId,
at: entry.at,
},
)
const result = await ctx.runMutation(internal.maintenance.upsertSkillBadgeRecordInternal, {
skillId: item.skillId,
kind: entry.kind,
byUserId: entry.byUserId,
at: entry.at,
})
if (result.inserted) {
totals.recordsInserted++
}
Expand Down Expand Up @@ -814,9 +812,10 @@ export const backfillSkillBadgeTable: ReturnType<typeof action> = action({
handler: async (ctx, args): Promise<SkillBadgeTableBackfillActionResult> => {
const { user } = await requireUserFromAction(ctx)
assertRole(user, ['admin'])
return ctx.runAction(internal.maintenance.backfillSkillBadgeTableInternal, args) as Promise<
SkillBadgeTableBackfillActionResult
>
return ctx.runAction(
internal.maintenance.backfillSkillBadgeTableInternal,
args,
) as Promise<SkillBadgeTableBackfillActionResult>
},
})

Expand Down
7 changes: 7 additions & 0 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ const skillStatEvents = defineTable({
.index('by_unprocessed', ['processedAt'])
.index('by_skill', ['skillId'])

const skillStatUpdateCursors = defineTable({
key: v.string(),
cursorCreationTime: v.optional(v.number()),
updatedAt: v.number(),
}).index('by_key', ['key'])

const soulEmbeddings = defineTable({
soulId: v.id('souls'),
versionId: v.id('soulVersions'),
Expand Down Expand Up @@ -450,6 +456,7 @@ export default defineSchema({
skillLeaderboards,
skillStatBackfillState,
skillStatEvents,
skillStatUpdateCursors,
comments,
skillReports,
soulComments,
Expand Down
24 changes: 20 additions & 4 deletions convex/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { v } from 'convex/values'
import { internal } from './_generated/api'
import type { Doc, Id } from './_generated/dataModel'
import { action, internalQuery } from './_generated/server'
import { generateEmbedding } from './lib/embeddings'
import { getSkillBadgeMaps, isSkillHighlighted, type SkillBadgeMap } from './lib/badges'
import { matchesExactTokens, tokenize } from './lib/searchText'
import { generateEmbedding } from './lib/embeddings'
import { toPublicSkill, toPublicSoul } from './lib/public'
import { matchesExactTokens, tokenize } from './lib/searchText'

type HydratedEntry = {
embeddingId: Id<'skillEmbeddings'>
Expand Down Expand Up @@ -79,7 +79,11 @@ export const searchSkills: ReturnType<typeof action> = action({
: hydratedWithBadges

exactMatches = filtered.filter((entry) =>
matchesExactTokens(queryTokens, [entry.skill.displayName, entry.skill.slug, entry.skill.summary]),
matchesExactTokens(queryTokens, [
entry.skill.displayName,
entry.skill.slug,
entry.skill.summary,
]),
)

if (exactMatches.length >= limit || results.length < candidateLimit) {
Expand All @@ -101,6 +105,14 @@ export const searchSkills: ReturnType<typeof action> = action({
},
})

export const getBadgeMapsForSkills = internalQuery({
args: { skillIds: v.array(v.id('skills')) },
handler: async (ctx, args): Promise<Array<[Id<'skills'>, SkillBadgeMap]>> => {
const badgeMap = await getSkillBadgeMaps(ctx, args.skillIds)
return Array.from(badgeMap.entries())
},
})

export const hydrateResults = internalQuery({
args: { embeddingIds: v.array(v.id('skillEmbeddings')) },
handler: async (ctx, args): Promise<HydratedEntry[]> => {
Expand Down Expand Up @@ -185,7 +197,11 @@ export const searchSouls: ReturnType<typeof action> = action({
)

exactMatches = hydrated.filter((entry) =>
matchesExactTokens(queryTokens, [entry.soul.displayName, entry.soul.slug, entry.soul.summary]),
matchesExactTokens(queryTokens, [
entry.soul.displayName,
entry.soul.slug,
entry.soul.summary,
]),
)

if (exactMatches.length >= limit || results.length < candidateLimit) {
Expand Down
Loading