Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ query GetKbKnowledgeGraphConfig($kbId: ID!) {
sourceContentDigest
activeBuildId
publishedBuildId
elementGenerationReady
isStale
startedAt
finishedAt
Expand Down
1 change: 1 addition & 0 deletions packages/graphql/src/public/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -2215,6 +2215,7 @@ type KBKnowledgeGraphConfig {
costCurrency: String
costStatus: KBGraphCostStatus
createdAt: Date
elementGenerationReady: Boolean!
estimatedCostMinorUnits: Int
finishedAt: Date
highEstimateMinorUnits: Int
Expand Down
1 change: 1 addition & 0 deletions packages/graphql/src/schema/kbKnowledgeGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const KBKnowledgeGraphConfigType = KBKnowledgeGraphConfigRef.implement({
}),
activeBuildId: t.exposeID('activeBuildId', { nullable: true }),
publishedBuildId: t.exposeID('publishedBuildId', { nullable: true }),
elementGenerationReady: t.exposeBoolean('elementGenerationReady'),
isStale: t.exposeBoolean('isStale'),
startedAt: t.expose('startedAt', { type: 'Date', nullable: true }),
finishedAt: t.expose('finishedAt', { type: 'Date', nullable: true }),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as DB from '@klicker-uzh/prisma/client'

export interface ElementGenerationGraphBundle {
status: DB.KBGraphBuildStatus
graphBundleContainerName: string | null
graphBundleBlobPrefix: string | null
graphBundleStorageName: string | null
graphBundleSha256: string | null
graphSha256: string | null
graphManifestSchemaVersion: number | null
graphManifestArtifact: DB.Prisma.JsonValue | null
}

type ReadyElementGenerationGraphBundle<T extends ElementGenerationGraphBundle> =
T & {
status: typeof DB.KBGraphBuildStatus.SUCCEEDED
graphBundleContainerName: string
graphBundleBlobPrefix: string
graphBundleStorageName: string
graphBundleSha256: string
graphSha256: string
graphManifestSchemaVersion: 2
graphManifestArtifact: Exclude<T['graphManifestArtifact'], null>
}

export function isElementGenerationGraphBundleReady<
T extends ElementGenerationGraphBundle,
>(build: T | null | undefined): build is ReadyElementGenerationGraphBundle<T> {
return (
build?.status === DB.KBGraphBuildStatus.SUCCEEDED &&
build.graphBundleContainerName !== null &&
build.graphBundleBlobPrefix !== null &&
build.graphBundleStorageName !== null &&
build.graphBundleSha256 !== null &&
build.graphSha256 !== null &&
build.graphManifestSchemaVersion === 2 &&
build.graphManifestArtifact !== null
)
}
49 changes: 45 additions & 4 deletions packages/graphql/src/services/knowledge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { GraphQLError } from 'graphql'
import { validate as validateUuid } from 'uuid'
import type { ContextWithUser } from '../lib/context.js'
import { assertManageAiEnabled } from '../lib/manageAiFeatureGate.js'
import { isElementGenerationGraphBundleReady } from './elementGenerationGraphReadiness.js'
import { getKBGraphBundleCoordinates } from './kbGraphBundleCoordinates.js'
import {
getKBGraphRemainingQuota,
Expand Down Expand Up @@ -1792,6 +1793,7 @@ export interface KBKnowledgeGraphConfig {
sourceContentDigest: string | null
activeBuildId: string | null
publishedBuildId: string | null
elementGenerationReady: boolean
isStale: boolean
startedAt: Date | null
finishedAt: Date | null
Expand Down Expand Up @@ -1893,7 +1895,8 @@ export function getKBGraphBuildConfig(
reservedMinorUnits: number
settledMinorUnits: number
} | null,
costConfiguration: ReturnType<typeof getKBGraphCostConfiguration>
costConfiguration: ReturnType<typeof getKBGraphCostConfiguration>,
elementGenerationReady: boolean
): KBKnowledgeGraphConfig {
const quotaConfigurationMatches =
quota === null ||
Expand All @@ -1920,6 +1923,7 @@ export function getKBGraphBuildConfig(
sourceContentDigest: build?.sourceContentDigest ?? null,
activeBuildId: kb.activeGraphBuildId,
publishedBuildId: kb.publishedGraphBuildId,
elementGenerationReady,
isStale,
startedAt: build?.startedAt ?? null,
finishedAt: build?.finishedAt ?? null,
Expand Down Expand Up @@ -1971,7 +1975,17 @@ export async function getKbKnowledgeGraphConfig(
kbId: kb.id,
status: DB.KBGraphBuildStatus.SUCCEEDED,
},
select: { sourceContentDigest: true },
select: {
sourceContentDigest: true,
status: true,
graphBundleContainerName: true,
graphBundleBlobPrefix: true,
graphBundleStorageName: true,
graphBundleSha256: true,
graphSha256: true,
graphManifestSchemaVersion: true,
graphManifestArtifact: true,
},
})
: Promise.resolve(null),
])
Expand All @@ -1994,7 +2008,14 @@ export async function getKbKnowledgeGraphConfig(
? publishedBuild.sourceContentDigest !==
(await computeKBContentDigest(ctx.prisma, kb.id))
: false
return getKBGraphBuildConfig(kb, build, isStale, quota, costConfiguration)
return getKBGraphBuildConfig(
kb,
build,
isStale,
quota,
costConfiguration,
isElementGenerationGraphBundleReady(publishedBuild)
)
}

async function readOwnedPublishedKBGraph(
Expand Down Expand Up @@ -2295,11 +2316,31 @@ export async function rebuildKbKnowledgeGraph(
settledMinorUnits: true,
},
})
const publishedBuild = result.kb.publishedGraphBuildId
? await ctx.prisma.kBGraphBuild.findFirst({
where: {
id: result.kb.publishedGraphBuildId,
kbId,
status: DB.KBGraphBuildStatus.SUCCEEDED,
},
select: {
status: true,
graphBundleContainerName: true,
graphBundleBlobPrefix: true,
graphBundleStorageName: true,
graphBundleSha256: true,
graphSha256: true,
graphManifestSchemaVersion: true,
graphManifestArtifact: true,
},
})
: null
return getKBGraphBuildConfig(
result.kb,
result.build,
isStale,
quota,
costConfiguration
costConfiguration,
isElementGenerationGraphBundleReady(publishedBuild)
)
}
14 changes: 5 additions & 9 deletions packages/graphql/src/services/questionGenerationGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import {
getPublishedKnowledgeGraph,
KnowledgeGraphNotPublishedError,
} from '@klicker-uzh/knowledge-graph'
import * as DB from '@klicker-uzh/prisma/client'
import type * as DB from '@klicker-uzh/prisma/client'
import type {
KBGraphSourceSnapshot,
QuestionGenerationArtifactRef,
} from '@klicker-uzh/types'
import { QUESTION_GENERATION_CAPABILITIES } from '@klicker-uzh/types'
import type { ContextWithUser } from '../lib/context.js'
import { assertManageAiEnabled } from '../lib/manageAiFeatureGate.js'
import { isElementGenerationGraphBundleReady } from './elementGenerationGraphReadiness.js'

export type QuestionGenerationGraphErrorCode =
| 'KB_GRAPH_VERSION_NOT_ELIGIBLE'
Expand Down Expand Up @@ -116,6 +117,8 @@ const nativeBuildSelect = {
kbId: true,
status: true,
graphName: true,
graphBundleContainerName: true,
graphBundleBlobPrefix: true,
graphBundleStorageName: true,
graphBundleSha256: true,
graphSha256: true,
Expand Down Expand Up @@ -143,14 +146,7 @@ function asGenerationGraph(
build: NativeBuild,
isStale: boolean
): QuestionGenerationGraph {
if (
build.status !== DB.KBGraphBuildStatus.SUCCEEDED ||
build.graphBundleStorageName === null ||
build.graphBundleSha256 === null ||
build.graphSha256 === null ||
build.graphManifestSchemaVersion !== 2 ||
build.graphManifestArtifact === null
) {
if (!isElementGenerationGraphBundleReady(build)) {
throw graphError(
'KB_GRAPH_VERSION_NOT_ELIGIBLE',
'Published knowledge graph does not have a generation bundle'
Expand Down
39 changes: 39 additions & 0 deletions packages/graphql/test/elementGenerationGraphReadiness.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { KBGraphBuildStatus, type Prisma } from '@klicker-uzh/prisma/client'
import { describe, expect, it } from 'vitest'
import { isElementGenerationGraphBundleReady } from '../src/services/elementGenerationGraphReadiness.js'

const readyBuild = {
status: KBGraphBuildStatus.SUCCEEDED,
graphBundleContainerName: 'kg-graph-artifacts',
graphBundleBlobPrefix: 'graph-artifacts/build/build',
graphBundleStorageName: 'bundle-storage',
graphBundleSha256: 'a'.repeat(64),
graphSha256: 'b'.repeat(64),
graphManifestSchemaVersion: 2,
graphManifestArtifact: {
containerName: 'graphs',
blobName: 'manifest.json',
sha256: 'c'.repeat(64),
} satisfies Prisma.JsonObject,
}

describe('element generation graph readiness', () => {
it('accepts a succeeded native graph build with a complete v2 bundle', () => {
expect(isElementGenerationGraphBundleReady(readyBuild)).toBe(true)
})

it.each([
{ status: KBGraphBuildStatus.PROCESSING },
{ graphBundleContainerName: null },
{ graphBundleBlobPrefix: null },
{ graphBundleStorageName: null },
{ graphBundleSha256: null },
{ graphSha256: null },
{ graphManifestSchemaVersion: 1 },
{ graphManifestArtifact: null },
])('rejects an ineligible native build: %o', (override) => {
expect(
isElementGenerationGraphBundleReady({ ...readyBuild, ...override })
).toBe(false)
})
})
4 changes: 3 additions & 1 deletion packages/graphql/test/knowledgeGraphConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,14 @@ describe('KB knowledge graph config', () => {
reservedMinorUnits: 100,
settledMinorUnits: 50,
},
costConfiguration
costConfiguration,
true
)

expect(result.costConfigurationReady).toBe(false)
expect(result.costCurrency).toBe('EUR')
expect(result.quotaCurrency).toBe('USD')
expect(result.remainingSemesterQuotaMinorUnits).toBe(750)
expect(result.elementGenerationReady).toBe(true)
})
})
Loading