feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
102
server/supabase/functions/_shared/meeting-document-contract.ts
Normal file
102
server/supabase/functions/_shared/meeting-document-contract.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
export interface GenerateMeetingDocumentRequest {
|
||||
meetingId: string
|
||||
templateId: string
|
||||
idempotencyKey: string
|
||||
title: string
|
||||
model: 'claude-haiku-4-5-20251001' | 'claude-sonnet-4-6' | 'claude-opus-4-6'
|
||||
}
|
||||
|
||||
export interface ProviderDocumentResult {
|
||||
content: string
|
||||
inputTokens: number | null
|
||||
outputTokens: number | null
|
||||
}
|
||||
|
||||
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
|
||||
const MODELS = new Set<GenerateMeetingDocumentRequest['model']>([
|
||||
'claude-haiku-4-5-20251001',
|
||||
'claude-sonnet-4-6',
|
||||
'claude-opus-4-6',
|
||||
])
|
||||
const REQUEST_KEYS = new Set([
|
||||
'meetingId',
|
||||
'templateId',
|
||||
'idempotencyKey',
|
||||
'title',
|
||||
'model',
|
||||
])
|
||||
|
||||
export class MeetingDocumentRequestError extends Error {
|
||||
readonly status = 400
|
||||
|
||||
constructor(message: string) {
|
||||
super(message)
|
||||
this.name = 'MeetingDocumentRequestError'
|
||||
}
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function requireUuid(value: unknown, field: string): string {
|
||||
if (typeof value !== 'string' || !UUID_PATTERN.test(value)) {
|
||||
throw new MeetingDocumentRequestError(`${field} must be a UUID`)
|
||||
}
|
||||
return value.toLowerCase()
|
||||
}
|
||||
|
||||
export function parseGenerateMeetingDocumentRequest(
|
||||
value: unknown,
|
||||
): GenerateMeetingDocumentRequest {
|
||||
if (!isRecord(value)) throw new MeetingDocumentRequestError('Invalid JSON body')
|
||||
const unsupported = Object.keys(value).filter((key) => !REQUEST_KEYS.has(key))
|
||||
if (unsupported.length > 0) {
|
||||
throw new MeetingDocumentRequestError('Unsupported request fields')
|
||||
}
|
||||
|
||||
const title = typeof value.title === 'string'
|
||||
? value.title.trim().replace(/\s+/g, ' ')
|
||||
: ''
|
||||
if (title.length < 1 || title.length > 160) {
|
||||
throw new MeetingDocumentRequestError('title must contain 1-160 characters')
|
||||
}
|
||||
|
||||
const model = value.model === undefined
|
||||
? 'claude-haiku-4-5-20251001'
|
||||
: value.model
|
||||
if (typeof model !== 'string' || !MODELS.has(model as GenerateMeetingDocumentRequest['model'])) {
|
||||
throw new MeetingDocumentRequestError('Unsupported model')
|
||||
}
|
||||
|
||||
return {
|
||||
meetingId: requireUuid(value.meetingId, 'meetingId'),
|
||||
templateId: requireUuid(value.templateId, 'templateId'),
|
||||
idempotencyKey: requireUuid(value.idempotencyKey, 'idempotencyKey'),
|
||||
title,
|
||||
model: model as GenerateMeetingDocumentRequest['model'],
|
||||
}
|
||||
}
|
||||
|
||||
function safeTokenCount(value: unknown): number | null {
|
||||
return Number.isSafeInteger(value) && Number(value) >= 0 ? Number(value) : null
|
||||
}
|
||||
|
||||
export function parseProviderDocumentResult(value: unknown): ProviderDocumentResult | null {
|
||||
if (!isRecord(value) || !Array.isArray(value.content)) return null
|
||||
const text = value.content
|
||||
.filter((block): block is Record<string, unknown> => isRecord(block))
|
||||
.filter((block) => block.type === 'text' && typeof block.text === 'string')
|
||||
.map((block) => (block.text as string).trim())
|
||||
.filter((part) => part.length > 0)
|
||||
.join('\n\n')
|
||||
.trim()
|
||||
if (text.length < 1 || text.length > 100000) return null
|
||||
|
||||
const usage = isRecord(value.usage) ? value.usage : {}
|
||||
return {
|
||||
content: text,
|
||||
inputTokens: safeTokenCount(usage.input_tokens),
|
||||
outputTokens: safeTokenCount(usage.output_tokens),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue