create-wirejs-app 2.0.168-llm → 2.0.170

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.
@@ -0,0 +1,42 @@
1
+ import { LLMMessage, ToolDefinition as BaseToolDefinition } from "wirejs-resources";
2
+
3
+ export type Role = 'assistant' | 'user' | 'step' | 'tool';
4
+
5
+ export type Chunk = {
6
+ mid: number;
7
+ seq: number;
8
+ pad: string; // security padding
9
+ data: ChunkData;
10
+ }
11
+
12
+ export type ChunkData =
13
+ | { type: 'start' }
14
+ | { type: 'end' }
15
+ | { type: 'title', value: string }
16
+ | { type: 'status', status: string }
17
+ | { type: 'text', text: string, role: Role }
18
+ ;
19
+
20
+ export type Conversation = {
21
+ userId: string;
22
+ conversationId: string;
23
+ name: string;
24
+ createdAt: number;
25
+ };
26
+
27
+ export type WorkflowStep = {
28
+ role: 'step';
29
+ content: string;
30
+ }
31
+
32
+ export type ConversationMessage = (WorkflowStep | LLMMessage) & {
33
+ conversationId: string;
34
+ mid: number;
35
+ createdAt: number;
36
+ };
37
+
38
+ export type ToolDefinition = BaseToolDefinition & {
39
+ execute: (...args: any) => Promise<any>
40
+ };
41
+
42
+ export type Message = LLMMessage;
@@ -0,0 +1,164 @@
1
+
2
+ import { randomUUID } from 'crypto';
3
+ import { JSDOM } from 'jsdom';
4
+
5
+ /**
6
+ * Generate a random number of random characters.
7
+ *
8
+ * Intended for inclusion in chunks sent over the wire for security purposes. Chunks
9
+ * are otherwise more susceptible to snooping.
10
+ *
11
+ * @returns
12
+ */
13
+ export const pad = () => randomUUID().slice(0, 1 + Math.floor(Math.random() * 16));
14
+
15
+ /**
16
+ * Generate a 64 bit integer ID based on time and randomness. Suitable for
17
+ * non-globally unique IDs (sub-IDs) that just need to be sortable based on
18
+ * create or update time.
19
+ *
20
+ * @returns
21
+ */
22
+ export const intId = () => Math.floor((Date.now() + Math.random()) * 10_000);
23
+
24
+ /**
25
+ * Cleans up quotes from a title that may have been provided by an LLM.
26
+ *
27
+ * @param title
28
+ * @returns
29
+ */
30
+ export const cleanTitle = (title: string) => {
31
+ let cleanTitle = title.trim();
32
+ if ((cleanTitle.startsWith('"') && cleanTitle.endsWith('"')) ||
33
+ (cleanTitle.startsWith("'") && cleanTitle.endsWith("'"))) {
34
+ cleanTitle = cleanTitle.slice(1, -1).trim();
35
+ }
36
+ return cleanTitle;
37
+ }
38
+
39
+ /**
40
+ * Extract "content"-like text from HTML using common patterns to ignore headers, footers,
41
+ * navigation, extra whitespace, etc.
42
+ */
43
+ export const extractContentFromHtml = (html: string): string => {
44
+ if (!html.includes('<html') && !html.includes('<!DOCTYPE')) {
45
+ return html;
46
+ }
47
+
48
+ try {
49
+ console.log(`[HTML] Starting JSDOM extraction from ${html.length} chars`);
50
+
51
+ // Load HTML into JSDOM for DOM manipulation
52
+ const dom = new JSDOM(html);
53
+ const document = dom.window.document;
54
+
55
+ // Remove unwanted elements entirely using querySelectorAll and remove
56
+ const removeSelectors = [
57
+ 'script, style, nav, header, footer, aside',
58
+ '.mw-navigation, .navbox, .infobox, .sidebar', // Wikipedia-specific
59
+ '[class*="nav"], [class*="menu"], [class*="sidebar"]', // Common patterns
60
+ '.reference, .citation, sup.reference', // Citations
61
+ '.printfooter, .catlinks', // Wikipedia footer stuff
62
+ 'table.ambox, .hatnote' // Wikipedia message boxes
63
+ ];
64
+
65
+ removeSelectors.forEach(selector => {
66
+ document.querySelectorAll(selector).forEach(element => element.remove());
67
+ });
68
+
69
+ // Extract text with cleaned up whitespace
70
+ const bodyElement = document.body;
71
+ let text = (bodyElement?.textContent || '')
72
+ .replace(/\s+/g, ' ') // Normalize whitespace
73
+ .replace(/\[\d+\]/g, '') // Remove citation numbers [1], [2], etc.
74
+ .replace(/\s*\n\s*/g, '\n') // Clean line breaks
75
+ .replace(/\n{3,}/g, '\n\n') // Limit consecutive newlines
76
+ .trim();
77
+
78
+ console.log(`[HTML] Final JSDOM extracted text: ${text.length} chars`);
79
+ return text;
80
+ } catch (error) {
81
+ console.error('Error extracting text with JSDOM:', error);
82
+ // Fallback to simple regex approach
83
+ console.log('[HTML] Falling back to regex extraction');
84
+ return html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
85
+ }
86
+ };
87
+
88
+ /**
89
+ * Convert async generator to array (for Node 20 compatibility)
90
+ */
91
+ export async function fromAsync<T>(gen: AsyncGenerator<T>): Promise<T[]> {
92
+ const items: T[] = [];
93
+ for await (const item of gen) {
94
+ items.push(item);
95
+ }
96
+ return items;
97
+ }
98
+
99
+ /**
100
+ * Removes leading whitespace on every line of input text.
101
+ *
102
+ * This works by finding the minimum indent depth of a block of multi-line text and
103
+ * left-trimming every line by the common (minimum) indent amount.
104
+ *
105
+ * @param content
106
+ * @returns
107
+ */
108
+ function dedentString(content: string) {
109
+ return dedentLines(content.split('\n')).join('\n');
110
+ }
111
+
112
+ function findMinimumIndent(lines: string[]) {
113
+ let minimumIndent: number | undefined = undefined;
114
+ for (const line of lines) {
115
+ if (line.trim() === '') continue;
116
+
117
+ const whitespace = line.match(/^\s+/)?.[0];
118
+ if (!whitespace) continue;
119
+
120
+ if (minimumIndent === undefined || whitespace.length < minimumIndent) {
121
+ minimumIndent = whitespace.length;
122
+ }
123
+ }
124
+ return minimumIndent;
125
+ }
126
+
127
+ function dedentLines(lines: string[]) {
128
+ const output: string[] = [];
129
+ const minimumIndent = findMinimumIndent(lines);
130
+ for (const line of lines) {
131
+ output.push(line.substring(minimumIndent ?? 0));
132
+ }
133
+ return output;
134
+ }
135
+
136
+ export function dedent(content: TemplateStringsArray, ...values: any[]) {
137
+ const PH = '%%%___DEDENT_PLACEHOLDER___%%%';
138
+ const combined = Array.from(content).join(PH);
139
+ const dedentedContent = dedentString(combined).split(PH);
140
+ return String.raw({ raw: dedentedContent }, ...values);
141
+ }
142
+
143
+ export function parseLLMJson(raw: string) {
144
+ if (!raw || typeof raw !== "string") {
145
+ throw new Error("Empty or non-string LLM output");
146
+ }
147
+
148
+ let text = raw.trim();
149
+
150
+ if (text.startsWith("```")) {
151
+ text = text.replace(/^```[a-zA-Z]*\s*/, "");
152
+ text = text.replace(/```$/, "");
153
+ text = text.trim();
154
+ }
155
+
156
+ if (text.toLowerCase().startsWith("json")) {
157
+ const after = text.slice(4).trim();
158
+ if (after.startsWith("{")) {
159
+ text = after;
160
+ }
161
+ }
162
+
163
+ return JSON.parse(text);
164
+ }
@@ -4,12 +4,12 @@ import { Todos } from './apps/todos.js';
4
4
  import { Wiki } from './apps/wiki.js';
5
5
  import { Store } from './apps/store.js';
6
6
  import { Admin } from './apps/admin.js';
7
- import { LLM } from './apps/llm.js';
7
+ import { LLM } from './apps/llm/index.js';
8
8
 
9
+ export type * from './apps/llm/index.js';
9
10
  export type * from './apps/todos.js';
10
11
  export type * from './apps/store.js';
11
12
  export type * from './apps/admin.js';
12
- export type * from './apps/llm.js';
13
13
 
14
14
  const authService = new AuthenticationService('app', 'core-users');
15
15
 
@@ -22,16 +22,18 @@ export const admin = Admin(auth);
22
22
  export const llm = LLM(auth);
23
23
 
24
24
  new Endpoint('app', 'sample-endpoint', {
25
- description: "Sample endpoint to show dynamic endpoint creation.",
26
- handle() {
25
+ description: "Sample endpoint to show programmatic endpoint creation.",
26
+ handle(context) {
27
+ context.responseHeaders['Content-Type'] = 'text/html; charset=utf-8';
27
28
  return "<html><body><p>Hello!</p><p><a href='/'>Back.</a></body></html>";
28
29
  }
29
30
  });
30
31
 
31
32
  new Endpoint('app', 'sample-wildcard-endpoint', {
32
33
  path: 'wildcard-endpoint/%',
33
- description: "Sample endpoint to show dynamic wildcard endpoint creation.",
34
+ description: "Sample endpoint to show programmatic wildcard endpoint creation.",
34
35
  handle(context) {
36
+ context.responseHeaders['Content-Type'] = 'text/html; charset=utf-8';
35
37
  return `<html>
36
38
  <body>
37
39
  <h2>${context.location.toString()
@@ -42,4 +44,4 @@ new Endpoint('app', 'sample-wildcard-endpoint', {
42
44
  </body>
43
45
  </html>`;
44
46
  }
45
- });
47
+ });
@@ -11,5 +11,13 @@
11
11
  "types": "./index.ts",
12
12
  "wirejs:client": "./dist/index.client.js",
13
13
  "default": "./dist/index.js"
14
+ },
15
+ "dependencies": {
16
+ "jsdom": "^25.0.0",
17
+ "wirejs-module-payments-stripe": "*",
18
+ "wirejs-resources": "*"
19
+ },
20
+ "devDependencies": {
21
+ "@types/jsdom": "^21.0.0"
14
22
  }
15
23
  }
@@ -0,0 +1,6 @@
1
+ import { DeploymentConfig } from 'wirejs-resources';
2
+
3
+ export default {
4
+ runtimeDesiredMemoryMB: 1 * 1024,
5
+ bundleNodeModules: ['jsdom'],
6
+ } satisfies DeploymentConfig;
@@ -12,13 +12,13 @@
12
12
  "dompurify": "^3.2.3",
13
13
  "marked": "^15.0.6",
14
14
  "wirejs-dom": "^1.0.44",
15
- "wirejs-resources": "^0.1.163-llm",
16
- "wirejs-components": "^0.1.106-llm",
17
- "wirejs-module-payments-stripe": "^0.1.57-llm",
18
- "wirejs-web-worker": "^1.0.60-llm"
15
+ "wirejs-resources": "^0.1.165",
16
+ "wirejs-components": "^0.1.108",
17
+ "wirejs-module-payments-stripe": "^0.1.59",
18
+ "wirejs-web-worker": "^1.0.62"
19
19
  },
20
20
  "devDependencies": {
21
- "wirejs-scripts": "^3.0.161-llm",
21
+ "wirejs-scripts": "^3.0.163",
22
22
  "typescript": "^5.7.3"
23
23
  },
24
24
  "scripts": {
@@ -9,11 +9,11 @@ export async function generate() {
9
9
  <p>It comes with some sample API methods and pages.</p>
10
10
  <ul>
11
11
  <li><a href='/todo-app.html'>Todo App</a></li>
12
- <li><a href='/simple-wiki/index.html'>Simple Wiki</a></li>
12
+ <li><a href='/simple-wiki/index'>Simple Wiki</a></li>
13
13
  <li><a href='/realtime-test.html'>Realtime Test</a></li>
14
14
  <li><a href='/web-worker-test.html'>Web Worker Test</a></li>
15
15
  <li><a href='/storefront.html'>Storefront</a></li>
16
- <li><a href='/chatbot.html'>Chatbot</a></li>
16
+ <li><a href='/llm-test.html'>LLM Test</a></li>
17
17
  <li><a href='/admin.html'>Admin</a></li>
18
18
  </ul>
19
19
  </div>`