@realtimex/folio 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +20 -0
- package/README.md +63 -0
- package/api/server.ts +130 -0
- package/api/src/config/index.ts +96 -0
- package/api/src/middleware/auth.ts +128 -0
- package/api/src/middleware/errorHandler.ts +88 -0
- package/api/src/middleware/index.ts +4 -0
- package/api/src/middleware/rateLimit.ts +71 -0
- package/api/src/middleware/validation.ts +58 -0
- package/api/src/routes/accounts.ts +142 -0
- package/api/src/routes/baseline-config.ts +124 -0
- package/api/src/routes/chat.ts +154 -0
- package/api/src/routes/health.ts +61 -0
- package/api/src/routes/index.ts +35 -0
- package/api/src/routes/ingestions.ts +275 -0
- package/api/src/routes/migrate.ts +112 -0
- package/api/src/routes/policies.ts +121 -0
- package/api/src/routes/processing.ts +90 -0
- package/api/src/routes/rules.ts +11 -0
- package/api/src/routes/sdk.ts +100 -0
- package/api/src/routes/settings.ts +80 -0
- package/api/src/routes/setup.ts +389 -0
- package/api/src/routes/stats.ts +81 -0
- package/api/src/routes/tts.ts +190 -0
- package/api/src/services/BaselineConfigService.ts +208 -0
- package/api/src/services/ChatService.ts +204 -0
- package/api/src/services/GoogleDriveService.ts +331 -0
- package/api/src/services/GoogleSheetsService.ts +1107 -0
- package/api/src/services/IngestionService.ts +1187 -0
- package/api/src/services/ModelCapabilityService.ts +248 -0
- package/api/src/services/PolicyEngine.ts +1625 -0
- package/api/src/services/PolicyLearningService.ts +527 -0
- package/api/src/services/PolicyLoader.ts +249 -0
- package/api/src/services/RAGService.ts +391 -0
- package/api/src/services/SDKService.ts +249 -0
- package/api/src/services/supabase.ts +113 -0
- package/api/src/utils/Actuator.ts +284 -0
- package/api/src/utils/actions/ActionHandler.ts +34 -0
- package/api/src/utils/actions/AppendToGSheetAction.ts +260 -0
- package/api/src/utils/actions/AutoRenameAction.ts +58 -0
- package/api/src/utils/actions/CopyAction.ts +120 -0
- package/api/src/utils/actions/CopyToGDriveAction.ts +64 -0
- package/api/src/utils/actions/LogCsvAction.ts +48 -0
- package/api/src/utils/actions/NotifyAction.ts +39 -0
- package/api/src/utils/actions/RenameAction.ts +57 -0
- package/api/src/utils/actions/WebhookAction.ts +58 -0
- package/api/src/utils/actions/utils.ts +293 -0
- package/api/src/utils/llmResponse.ts +61 -0
- package/api/src/utils/logger.ts +67 -0
- package/bin/folio-deploy.js +12 -0
- package/bin/folio-setup.js +45 -0
- package/bin/folio.js +65 -0
- package/dist/api/server.js +106 -0
- package/dist/api/src/config/index.js +81 -0
- package/dist/api/src/middleware/auth.js +93 -0
- package/dist/api/src/middleware/errorHandler.js +73 -0
- package/dist/api/src/middleware/index.js +4 -0
- package/dist/api/src/middleware/rateLimit.js +43 -0
- package/dist/api/src/middleware/validation.js +54 -0
- package/dist/api/src/routes/accounts.js +110 -0
- package/dist/api/src/routes/baseline-config.js +91 -0
- package/dist/api/src/routes/chat.js +114 -0
- package/dist/api/src/routes/health.js +52 -0
- package/dist/api/src/routes/index.js +31 -0
- package/dist/api/src/routes/ingestions.js +207 -0
- package/dist/api/src/routes/migrate.js +91 -0
- package/dist/api/src/routes/policies.js +86 -0
- package/dist/api/src/routes/processing.js +75 -0
- package/dist/api/src/routes/rules.js +8 -0
- package/dist/api/src/routes/sdk.js +80 -0
- package/dist/api/src/routes/settings.js +68 -0
- package/dist/api/src/routes/setup.js +315 -0
- package/dist/api/src/routes/stats.js +62 -0
- package/dist/api/src/routes/tts.js +178 -0
- package/dist/api/src/services/BaselineConfigService.js +168 -0
- package/dist/api/src/services/ChatService.js +166 -0
- package/dist/api/src/services/GoogleDriveService.js +280 -0
- package/dist/api/src/services/GoogleSheetsService.js +795 -0
- package/dist/api/src/services/IngestionService.js +990 -0
- package/dist/api/src/services/ModelCapabilityService.js +179 -0
- package/dist/api/src/services/PolicyEngine.js +1353 -0
- package/dist/api/src/services/PolicyLearningService.js +397 -0
- package/dist/api/src/services/PolicyLoader.js +159 -0
- package/dist/api/src/services/RAGService.js +295 -0
- package/dist/api/src/services/SDKService.js +212 -0
- package/dist/api/src/services/supabase.js +72 -0
- package/dist/api/src/utils/Actuator.js +225 -0
- package/dist/api/src/utils/actions/ActionHandler.js +1 -0
- package/dist/api/src/utils/actions/AppendToGSheetAction.js +191 -0
- package/dist/api/src/utils/actions/AutoRenameAction.js +49 -0
- package/dist/api/src/utils/actions/CopyAction.js +112 -0
- package/dist/api/src/utils/actions/CopyToGDriveAction.js +55 -0
- package/dist/api/src/utils/actions/LogCsvAction.js +42 -0
- package/dist/api/src/utils/actions/NotifyAction.js +32 -0
- package/dist/api/src/utils/actions/RenameAction.js +51 -0
- package/dist/api/src/utils/actions/WebhookAction.js +51 -0
- package/dist/api/src/utils/actions/utils.js +237 -0
- package/dist/api/src/utils/llmResponse.js +63 -0
- package/dist/api/src/utils/logger.js +51 -0
- package/dist/assets/index-DzN8-j-e.css +1 -0
- package/dist/assets/index-Uy-ai3Dh.js +113 -0
- package/dist/favicon.svg +31 -0
- package/dist/folio-logo.svg +46 -0
- package/dist/index.html +14 -0
- package/docs-dev/FPE-spec.md +196 -0
- package/docs-dev/folio-prd.md +47 -0
- package/docs-dev/foundation-checklist.md +30 -0
- package/docs-dev/hybrid-routing-architecture.md +205 -0
- package/docs-dev/ingestion-engine.md +69 -0
- package/docs-dev/port-from-email-automator.md +32 -0
- package/docs-dev/tech-spec.md +98 -0
- package/index.html +13 -0
- package/package.json +101 -0
- package/public/favicon.svg +31 -0
- package/public/folio-logo.svg +46 -0
- package/scripts/dev-task.mjs +51 -0
- package/scripts/get-latest-migration-timestamp.mjs +34 -0
- package/scripts/migrate.sh +91 -0
- package/supabase/.temp/cli-latest +1 -0
- package/supabase/.temp/gotrue-version +1 -0
- package/supabase/.temp/pooler-url +1 -0
- package/supabase/.temp/postgres-version +1 -0
- package/supabase/.temp/project-ref +1 -0
- package/supabase/.temp/rest-version +1 -0
- package/supabase/.temp/storage-migration +1 -0
- package/supabase/.temp/storage-version +1 -0
- package/supabase/config.toml +64 -0
- package/supabase/functions/_shared/auth.ts +35 -0
- package/supabase/functions/_shared/cors.ts +12 -0
- package/supabase/functions/_shared/supabaseAdmin.ts +17 -0
- package/supabase/functions/api-v1-settings/index.ts +66 -0
- package/supabase/functions/setup/index.ts +91 -0
- package/supabase/migrations/20260223000000_initial_foundation.sql +136 -0
- package/supabase/migrations/20260223000001_add_migration_rpc.sql +10 -0
- package/supabase/migrations/20260224000002_add_init_state_view.sql +20 -0
- package/supabase/migrations/20260224000003_port_user_creation_parity.sql +139 -0
- package/supabase/migrations/20260224000004_add_avatars_storage.sql +26 -0
- package/supabase/migrations/20260224000005_add_tts_and_embed_settings.sql +24 -0
- package/supabase/migrations/20260224000006_add_policies_table.sql +48 -0
- package/supabase/migrations/20260224000007_fix_migration_rpc.sql +9 -0
- package/supabase/migrations/20260224000008_add_ingestions_table.sql +42 -0
- package/supabase/migrations/20260225000000_setup_compatible_mode.sql +119 -0
- package/supabase/migrations/20260225000001_restore_ingestions.sql +49 -0
- package/supabase/migrations/20260225000002_add_ingestion_trace.sql +2 -0
- package/supabase/migrations/20260225000003_add_baseline_configs.sql +35 -0
- package/supabase/migrations/20260226000000_add_processing_events.sql +26 -0
- package/supabase/migrations/20260226000001_add_ingestion_file_hash.sql +10 -0
- package/supabase/migrations/20260226000002_add_dynamic_rag.sql +150 -0
- package/supabase/migrations/20260226000003_add_ingestion_summary.sql +4 -0
- package/supabase/migrations/20260226000004_add_ingestion_tags.sql +7 -0
- package/supabase/migrations/20260226000005_add_chat_tables.sql +60 -0
- package/supabase/migrations/20260227000000_harden_chat_messages_rls.sql +25 -0
- package/supabase/migrations/20260228000000_add_vision_model_capabilities.sql +8 -0
- package/supabase/migrations/20260228000001_add_policy_match_feedback.sql +51 -0
- package/supabase/migrations/29991231235959_test_migration.sql +0 -0
- package/supabase/templates/confirmation.html +76 -0
- package/supabase/templates/email-change.html +76 -0
- package/supabase/templates/invite.html +72 -0
- package/supabase/templates/magic-link.html +68 -0
- package/supabase/templates/recovery.html +82 -0
- package/tsconfig.api.json +16 -0
- package/tsconfig.json +25 -0
- package/vite.config.ts +146 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger.js";
|
|
2
|
+
import { SDKService } from "./SDKService.js";
|
|
3
|
+
const logger = createLogger("ModelCapabilityService");
|
|
4
|
+
export class ModelCapabilityService {
|
|
5
|
+
static SUPPORTED_TTL_DAYS = 180;
|
|
6
|
+
static UNSUPPORTED_TTL_DAYS = 30;
|
|
7
|
+
static resolveVisionSupport(settingsRow) {
|
|
8
|
+
const provider = (settingsRow?.llm_provider || SDKService.DEFAULT_LLM_PROVIDER).trim();
|
|
9
|
+
const model = (settingsRow?.llm_model || SDKService.DEFAULT_LLM_MODEL).trim();
|
|
10
|
+
const state = this.getVisionState(settingsRow?.vision_model_capabilities, provider, model);
|
|
11
|
+
return {
|
|
12
|
+
provider,
|
|
13
|
+
model,
|
|
14
|
+
state,
|
|
15
|
+
shouldAttempt: state !== "unsupported",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
static getVisionState(rawMap, provider, model) {
|
|
19
|
+
const map = this.normalizeCapabilityMap(rawMap);
|
|
20
|
+
const entry = map[this.capabilityKey(provider, model)];
|
|
21
|
+
if (!entry)
|
|
22
|
+
return "unknown";
|
|
23
|
+
if (entry.expires_at) {
|
|
24
|
+
const expiryTs = Date.parse(entry.expires_at);
|
|
25
|
+
if (Number.isFinite(expiryTs) && expiryTs <= Date.now()) {
|
|
26
|
+
return "unknown";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return entry.state;
|
|
30
|
+
}
|
|
31
|
+
static async learnVisionSuccess(opts) {
|
|
32
|
+
await this.writeCapability({
|
|
33
|
+
...opts,
|
|
34
|
+
state: "supported",
|
|
35
|
+
reason: "vision_request_succeeded",
|
|
36
|
+
ttlDays: this.SUPPORTED_TTL_DAYS,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
static async learnVisionFailure(opts) {
|
|
40
|
+
const classification = this.classifyVisionFailure(opts.error);
|
|
41
|
+
if (!classification.isCapabilityError) {
|
|
42
|
+
logger.info(`Vision failure for ${opts.provider}/${opts.model} treated as transient; leaving capability unknown`, {
|
|
43
|
+
reason: classification.reason,
|
|
44
|
+
});
|
|
45
|
+
return "unknown";
|
|
46
|
+
}
|
|
47
|
+
await this.writeCapability({
|
|
48
|
+
supabase: opts.supabase,
|
|
49
|
+
userId: opts.userId,
|
|
50
|
+
provider: opts.provider,
|
|
51
|
+
model: opts.model,
|
|
52
|
+
state: "unsupported",
|
|
53
|
+
reason: classification.reason,
|
|
54
|
+
ttlDays: this.UNSUPPORTED_TTL_DAYS,
|
|
55
|
+
});
|
|
56
|
+
return "unsupported";
|
|
57
|
+
}
|
|
58
|
+
static async writeCapability(opts) {
|
|
59
|
+
const { supabase, userId, provider, model, state, reason, ttlDays } = opts;
|
|
60
|
+
const { data, error: readErr } = await supabase
|
|
61
|
+
.from("user_settings")
|
|
62
|
+
.select("vision_model_capabilities")
|
|
63
|
+
.eq("user_id", userId)
|
|
64
|
+
.maybeSingle();
|
|
65
|
+
if (readErr) {
|
|
66
|
+
logger.warn("Failed to read user_settings for model capability write", { userId, readErr });
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const map = this.normalizeCapabilityMap(data?.vision_model_capabilities);
|
|
70
|
+
const now = new Date();
|
|
71
|
+
const expiresAt = new Date(now.getTime() + ttlDays * 24 * 60 * 60 * 1000).toISOString();
|
|
72
|
+
map[this.capabilityKey(provider, model)] = {
|
|
73
|
+
state,
|
|
74
|
+
learned_at: now.toISOString(),
|
|
75
|
+
expires_at: expiresAt,
|
|
76
|
+
reason,
|
|
77
|
+
};
|
|
78
|
+
const { error: writeErr } = await supabase
|
|
79
|
+
.from("user_settings")
|
|
80
|
+
.upsert({
|
|
81
|
+
user_id: userId,
|
|
82
|
+
vision_model_capabilities: map,
|
|
83
|
+
}, { onConflict: "user_id" });
|
|
84
|
+
if (writeErr) {
|
|
85
|
+
logger.warn("Failed to persist model capability state", { userId, provider, model, state, writeErr });
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
logger.info(`Updated model capability for ${provider}/${model}: ${state}`, { reason, ttlDays });
|
|
89
|
+
}
|
|
90
|
+
static normalizeCapabilityMap(rawMap) {
|
|
91
|
+
if (!rawMap || typeof rawMap !== "object" || Array.isArray(rawMap)) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
const parsed = rawMap;
|
|
95
|
+
const normalized = {};
|
|
96
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
97
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const state = String(value.state || "");
|
|
101
|
+
if (state !== "supported" && state !== "unsupported") {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const learnedAt = value.learned_at;
|
|
105
|
+
const expiresAt = value.expires_at;
|
|
106
|
+
const reason = value.reason;
|
|
107
|
+
normalized[key] = {
|
|
108
|
+
state,
|
|
109
|
+
learned_at: typeof learnedAt === "string" ? learnedAt : new Date(0).toISOString(),
|
|
110
|
+
expires_at: typeof expiresAt === "string" ? expiresAt : undefined,
|
|
111
|
+
reason: typeof reason === "string" ? reason : undefined,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return normalized;
|
|
115
|
+
}
|
|
116
|
+
static capabilityKey(provider, model) {
|
|
117
|
+
return `${provider.toLowerCase().trim()}:${model.toLowerCase().trim()}`;
|
|
118
|
+
}
|
|
119
|
+
static classifyVisionFailure(error) {
|
|
120
|
+
const message = this.errorToMessage(error).toLowerCase();
|
|
121
|
+
if (!message)
|
|
122
|
+
return { isCapabilityError: false, reason: "empty_error" };
|
|
123
|
+
const capabilityHints = [
|
|
124
|
+
"image_url",
|
|
125
|
+
"vision",
|
|
126
|
+
"multimodal",
|
|
127
|
+
"multi-modal",
|
|
128
|
+
"unsupported content type",
|
|
129
|
+
"unsupported message content",
|
|
130
|
+
"does not support images",
|
|
131
|
+
"model does not support image",
|
|
132
|
+
"invalid content type",
|
|
133
|
+
"invalid image",
|
|
134
|
+
"unrecognized content type",
|
|
135
|
+
];
|
|
136
|
+
if (capabilityHints.some((hint) => message.includes(hint))) {
|
|
137
|
+
return { isCapabilityError: true, reason: "capability_mismatch" };
|
|
138
|
+
}
|
|
139
|
+
const transientHints = [
|
|
140
|
+
"timeout",
|
|
141
|
+
"timed out",
|
|
142
|
+
"rate limit",
|
|
143
|
+
"too many requests",
|
|
144
|
+
"429",
|
|
145
|
+
"503",
|
|
146
|
+
"502",
|
|
147
|
+
"504",
|
|
148
|
+
"service unavailable",
|
|
149
|
+
"temporar",
|
|
150
|
+
"network",
|
|
151
|
+
"connection",
|
|
152
|
+
"unauthorized",
|
|
153
|
+
"forbidden",
|
|
154
|
+
"invalid api key",
|
|
155
|
+
];
|
|
156
|
+
if (transientHints.some((hint) => message.includes(hint))) {
|
|
157
|
+
return { isCapabilityError: false, reason: "transient_or_auth" };
|
|
158
|
+
}
|
|
159
|
+
return { isCapabilityError: false, reason: "unknown_error_class" };
|
|
160
|
+
}
|
|
161
|
+
static errorToMessage(error) {
|
|
162
|
+
if (error instanceof Error)
|
|
163
|
+
return error.message;
|
|
164
|
+
if (typeof error === "string")
|
|
165
|
+
return error;
|
|
166
|
+
if (error && typeof error === "object") {
|
|
167
|
+
const candidate = error;
|
|
168
|
+
if (typeof candidate.message === "string")
|
|
169
|
+
return candidate.message;
|
|
170
|
+
try {
|
|
171
|
+
return JSON.stringify(error);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return String(error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return String(error ?? "");
|
|
178
|
+
}
|
|
179
|
+
}
|