birdclaw 0.8.2 → 0.8.3
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/CHANGELOG.md +16 -0
- package/package.json +2 -1
- package/src/cli/command-context.ts +17 -0
- package/src/cli/register-analysis.ts +500 -0
- package/src/cli/register-compose.ts +40 -0
- package/src/cli/register-graph.ts +132 -0
- package/src/cli/register-inbox.ts +41 -0
- package/src/cli/register-storage.ts +106 -0
- package/src/cli.ts +30 -750
- package/src/components/AccountSwitcher.tsx +7 -15
- package/src/components/AvatarChip.tsx +1 -1
- package/src/components/AvatarPreload.ts +149 -0
- package/src/components/MarkdownCitations.tsx +680 -0
- package/src/components/MarkdownViewer.tsx +8 -674
- package/src/components/ProfileAnalysisClient.ts +191 -0
- package/src/components/ProfileAnalysisStream.tsx +16 -185
- package/src/components/ProfilePreview.tsx +2 -0
- package/src/components/links-controller.ts +162 -0
- package/src/components/links-model.ts +198 -0
- package/src/components/network-map-controller.ts +84 -0
- package/src/components/network-map-model.ts +255 -0
- package/src/components/useTimelineRouteData.ts +105 -235
- package/src/lib/analysis-runtime.ts +238 -0
- package/src/lib/api-client.ts +16 -215
- package/src/lib/api-contracts.ts +328 -0
- package/src/lib/archive-import-plan.ts +102 -0
- package/src/lib/archive-import.ts +170 -239
- package/src/lib/authored-live.ts +75 -120
- package/src/lib/backup.ts +335 -424
- package/src/lib/blocks-write.ts +30 -26
- package/src/lib/blocks.ts +18 -20
- package/src/lib/database-metrics.ts +88 -0
- package/src/lib/database-migrations.ts +34 -0
- package/src/lib/database-schema.ts +312 -0
- package/src/lib/database-writer.ts +69 -0
- package/src/lib/db.ts +84 -330
- package/src/lib/dm-read-model.ts +533 -0
- package/src/lib/dms-live.ts +34 -97
- package/src/lib/follow-graph.ts +17 -27
- package/src/lib/import-repository.ts +138 -0
- package/src/lib/inbox.ts +2 -1
- package/src/lib/live-sync-engine.ts +209 -0
- package/src/lib/live-transport-gateway.ts +128 -0
- package/src/lib/mention-threads-live.ts +90 -177
- package/src/lib/mentions-export.ts +1 -1
- package/src/lib/mentions-live.ts +57 -181
- package/src/lib/moderation-target.ts +15 -4
- package/src/lib/moderation-write.ts +1 -1
- package/src/lib/mutes-write.ts +30 -26
- package/src/lib/openai-response-runtime.ts +251 -0
- package/src/lib/paginated-sync.ts +93 -0
- package/src/lib/period-digest.ts +116 -304
- package/src/lib/profile-analysis.ts +36 -110
- package/src/lib/queries.ts +6 -2381
- package/src/lib/query-actions.ts +437 -0
- package/src/lib/query-client.tsx +47 -0
- package/src/lib/query-read-model-shared.ts +52 -0
- package/src/lib/query-read-models.ts +5 -0
- package/src/lib/query-resource.ts +41 -0
- package/src/lib/query-status.ts +164 -0
- package/src/lib/research.ts +1 -1
- package/src/lib/runtime-services.ts +20 -0
- package/src/lib/search-discussion.ts +75 -279
- package/src/lib/server-runtime-services.ts +30 -0
- package/src/lib/sqlite.ts +48 -12
- package/src/lib/streaming-ingestion.ts +240 -0
- package/src/lib/sync-cache.ts +6 -1
- package/src/lib/sync-plan.ts +175 -0
- package/src/lib/timeline-collections-live.ts +83 -257
- package/src/lib/timeline-live.ts +86 -236
- package/src/lib/timeline-read-model.ts +1191 -0
- package/src/lib/tweet-repository.ts +156 -0
- package/src/lib/tweet-search-live.ts +63 -167
- package/src/lib/web-sync.ts +67 -50
- package/src/lib/whois.ts +2 -1
- package/src/routes/__root.tsx +11 -8
- package/src/routes/api/action.tsx +1 -1
- package/src/routes/api/conversation.tsx +1 -1
- package/src/routes/api/query.tsx +32 -26
- package/src/routes/api/status.tsx +6 -4
- package/src/routes/api/sync.tsx +5 -2
- package/src/routes/blocks.tsx +97 -131
- package/src/routes/data-sources.tsx +17 -25
- package/src/routes/dms.tsx +167 -184
- package/src/routes/inbox.tsx +63 -57
- package/src/routes/links.tsx +31 -394
- package/src/routes/network-map.tsx +41 -344
- package/src/routes/rate-limits.tsx +17 -21
- package/src/lib/client-cache.ts +0 -109
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import {
|
|
5
|
+
createAnalysisRequestBody,
|
|
6
|
+
extractOpenAIResponseText,
|
|
7
|
+
parseHybridAnalysis,
|
|
8
|
+
requestHybridAnalysisEffect,
|
|
9
|
+
resolveAnalysisModelSettings,
|
|
10
|
+
} from "./analysis-runtime";
|
|
4
11
|
import { getNativeDb } from "./db";
|
|
5
12
|
import { runEffectPromise, tryPromise } from "./effect-runtime";
|
|
6
13
|
import { buildMediaJsonFromIncludes, countTweetMedia } from "./media-includes";
|
|
@@ -138,9 +145,6 @@ export type ProfileAnalysisStreamEvent =
|
|
|
138
145
|
| { type: "done"; result: ProfileAnalysisRunResult }
|
|
139
146
|
| { type: "error"; error: string };
|
|
140
147
|
|
|
141
|
-
const DEFAULT_MODEL = "gpt-5.5";
|
|
142
|
-
const DEFAULT_REASONING_EFFORT = "medium";
|
|
143
|
-
const DEFAULT_SERVICE_TIER = "priority";
|
|
144
148
|
const DEFAULT_MAX_TWEETS = 10_000;
|
|
145
149
|
const DEFAULT_MAX_PAGES = 100;
|
|
146
150
|
const DEFAULT_MAX_CONVERSATIONS = 80;
|
|
@@ -287,27 +291,15 @@ function resolveAccount(db: Database, accountId?: string) {
|
|
|
287
291
|
}
|
|
288
292
|
|
|
289
293
|
function modelFromOptions(options: ProfileAnalysisOptions) {
|
|
290
|
-
return options.model
|
|
294
|
+
return resolveAnalysisModelSettings(options).model;
|
|
291
295
|
}
|
|
292
296
|
|
|
293
297
|
function reasoningEffortFromOptions(options: ProfileAnalysisOptions) {
|
|
294
|
-
return (
|
|
295
|
-
options.reasoningEffort ??
|
|
296
|
-
(process.env.BIRDCLAW_OPENAI_REASONING_EFFORT as
|
|
297
|
-
| ProfileAnalysisOptions["reasoningEffort"]
|
|
298
|
-
| undefined) ??
|
|
299
|
-
DEFAULT_REASONING_EFFORT
|
|
300
|
-
);
|
|
298
|
+
return resolveAnalysisModelSettings(options).reasoningEffort;
|
|
301
299
|
}
|
|
302
300
|
|
|
303
301
|
function serviceTierFromOptions(options: ProfileAnalysisOptions) {
|
|
304
|
-
return (
|
|
305
|
-
options.serviceTier ??
|
|
306
|
-
(process.env.BIRDCLAW_OPENAI_SERVICE_TIER as
|
|
307
|
-
| ProfileAnalysisOptions["serviceTier"]
|
|
308
|
-
| undefined) ??
|
|
309
|
-
DEFAULT_SERVICE_TIER
|
|
310
|
-
);
|
|
302
|
+
return resolveAnalysisModelSettings(options).serviceTier;
|
|
311
303
|
}
|
|
312
304
|
|
|
313
305
|
function tweetUrl(handle: string, id: string) {
|
|
@@ -1092,66 +1084,30 @@ function parseAnalysisFromHybridText(
|
|
|
1092
1084
|
context: ProfileAnalysisContext,
|
|
1093
1085
|
rawText: string,
|
|
1094
1086
|
): { analysis: ProfileAnalysis; markdown: string } {
|
|
1095
|
-
const
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
);
|
|
1101
|
-
|
|
1102
|
-
try {
|
|
1103
|
-
return {
|
|
1104
|
-
markdown,
|
|
1105
|
-
analysis: ProfileAnalysisSchema.parse(JSON.parse(candidate)),
|
|
1106
|
-
};
|
|
1107
|
-
} catch {
|
|
1108
|
-
return { markdown, analysis: fallbackAnalysis(context, markdown) };
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
return { markdown, analysis: fallbackAnalysis(context, markdown) };
|
|
1087
|
+
const parsed = parseHybridAnalysis({
|
|
1088
|
+
rawText,
|
|
1089
|
+
parse: (value) => ProfileAnalysisSchema.parse(value),
|
|
1090
|
+
fallback: (markdown) => fallbackAnalysis(context, markdown),
|
|
1091
|
+
delimiterPattern: DELIMITER_PATTERN,
|
|
1092
|
+
});
|
|
1093
|
+
return { markdown: parsed.markdown, analysis: parsed.value };
|
|
1112
1094
|
}
|
|
1113
1095
|
|
|
1114
1096
|
function extractResponseText(payload: Record<string, unknown>) {
|
|
1115
|
-
|
|
1116
|
-
return payload.output_text;
|
|
1117
|
-
}
|
|
1118
|
-
const output = Array.isArray(payload.output) ? payload.output : [];
|
|
1119
|
-
const parts: string[] = [];
|
|
1120
|
-
for (const item of output) {
|
|
1121
|
-
if (!item || typeof item !== "object") continue;
|
|
1122
|
-
const content = (item as { content?: unknown }).content;
|
|
1123
|
-
if (!Array.isArray(content)) continue;
|
|
1124
|
-
for (const block of content) {
|
|
1125
|
-
if (!block || typeof block !== "object") continue;
|
|
1126
|
-
const record = block as Record<string, unknown>;
|
|
1127
|
-
if (typeof record.text === "string") parts.push(record.text);
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
return parts.join("");
|
|
1097
|
+
return extractOpenAIResponseText(payload);
|
|
1131
1098
|
}
|
|
1132
1099
|
|
|
1133
1100
|
function createOpenAIRequestBody(
|
|
1134
1101
|
context: ProfileAnalysisContext,
|
|
1135
1102
|
options: ProfileAnalysisOptions,
|
|
1136
1103
|
) {
|
|
1137
|
-
return {
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
{
|
|
1145
|
-
role: "system",
|
|
1146
|
-
content:
|
|
1147
|
-
"You are a precise X/Twitter profile analyst. Use only supplied data. Return Markdown plus the requested JSON after the delimiter.",
|
|
1148
|
-
},
|
|
1149
|
-
{
|
|
1150
|
-
role: "user",
|
|
1151
|
-
content: buildPrompt(context),
|
|
1152
|
-
},
|
|
1153
|
-
],
|
|
1154
|
-
};
|
|
1104
|
+
return createAnalysisRequestBody({
|
|
1105
|
+
settings: resolveAnalysisModelSettings(options),
|
|
1106
|
+
system:
|
|
1107
|
+
"You are a precise X/Twitter profile analyst. Use only supplied data. Return Markdown plus the requested JSON after the delimiter.",
|
|
1108
|
+
prompt: buildPrompt(context),
|
|
1109
|
+
stream: false,
|
|
1110
|
+
});
|
|
1155
1111
|
}
|
|
1156
1112
|
|
|
1157
1113
|
export function streamProfileAnalysisEffect(
|
|
@@ -1192,49 +1148,19 @@ export function streamProfileAnalysisEffect(
|
|
|
1192
1148
|
return result;
|
|
1193
1149
|
}
|
|
1194
1150
|
|
|
1195
|
-
const apiKey = process.env.OPENAI_API_KEY;
|
|
1196
|
-
if (!apiKey) {
|
|
1197
|
-
return yield* Effect.fail(new Error("OPENAI_API_KEY is not set"));
|
|
1198
|
-
}
|
|
1199
1151
|
handlers.onEvent?.({ type: "start", context, cached: false });
|
|
1200
1152
|
emitStatus(handlers, "Summarizing with AI", modelFromOptions(options));
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
},
|
|
1209
|
-
body: JSON.stringify(createOpenAIRequestBody(context, options)),
|
|
1210
|
-
}),
|
|
1211
|
-
);
|
|
1212
|
-
if (!response.ok) {
|
|
1213
|
-
const text = yield* tryProfilePromise(() => response.text());
|
|
1214
|
-
return yield* Effect.fail(
|
|
1215
|
-
new Error(
|
|
1216
|
-
`OpenAI request failed: ${String(response.status)} ${text.slice(
|
|
1217
|
-
0,
|
|
1218
|
-
400,
|
|
1219
|
-
)}`,
|
|
1220
|
-
),
|
|
1221
|
-
);
|
|
1222
|
-
}
|
|
1223
|
-
const payload = (yield* tryProfilePromise(() => response.json())) as Record<
|
|
1224
|
-
string,
|
|
1225
|
-
unknown
|
|
1226
|
-
>;
|
|
1227
|
-
const rawText = extractResponseText(payload);
|
|
1228
|
-
if (!rawText) {
|
|
1229
|
-
return yield* Effect.fail(new Error("OpenAI returned no output text"));
|
|
1230
|
-
}
|
|
1231
|
-
const parsed = yield* tryProfileSync(() =>
|
|
1232
|
-
parseAnalysisFromHybridText(context, rawText),
|
|
1233
|
-
);
|
|
1153
|
+
const analysisResponse = yield* requestHybridAnalysisEffect({
|
|
1154
|
+
body: createOpenAIRequestBody(context, options),
|
|
1155
|
+
signal: options.signal,
|
|
1156
|
+
parse: (value) => ProfileAnalysisSchema.parse(value),
|
|
1157
|
+
fallback: (markdown) => fallbackAnalysis(context, markdown),
|
|
1158
|
+
delimiterPattern: DELIMITER_PATTERN,
|
|
1159
|
+
});
|
|
1234
1160
|
const updatedAt = yield* tryProfileSync(() =>
|
|
1235
1161
|
writeSyncCache(resultCacheKey(context, options), {
|
|
1236
|
-
analysis:
|
|
1237
|
-
markdown:
|
|
1162
|
+
analysis: analysisResponse.value,
|
|
1163
|
+
markdown: analysisResponse.markdown,
|
|
1238
1164
|
model: modelFromOptions(options),
|
|
1239
1165
|
reasoningEffort: reasoningEffortFromOptions(options),
|
|
1240
1166
|
serviceTier: serviceTierFromOptions(options),
|
|
@@ -1242,8 +1168,8 @@ export function streamProfileAnalysisEffect(
|
|
|
1242
1168
|
);
|
|
1243
1169
|
const result: ProfileAnalysisRunResult = {
|
|
1244
1170
|
context,
|
|
1245
|
-
analysis:
|
|
1246
|
-
markdown:
|
|
1171
|
+
analysis: analysisResponse.value,
|
|
1172
|
+
markdown: analysisResponse.markdown,
|
|
1247
1173
|
model: modelFromOptions(options),
|
|
1248
1174
|
reasoningEffort: reasoningEffortFromOptions(options),
|
|
1249
1175
|
serviceTier: serviceTierFromOptions(options),
|