@flisk/analyze-tracking 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flisk/analyze-tracking",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Analyzes tracking code in a project and generates data schemas",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/schema.json CHANGED
@@ -71,6 +71,10 @@
71
71
  "unknown"
72
72
  ],
73
73
  "description": "Name of the platform where the event is sent"
74
+ },
75
+ "description": {
76
+ "type": "string",
77
+ "description": "Description of how the event is triggered"
74
78
  }
75
79
  },
76
80
  "required": [
@@ -89,6 +93,10 @@
89
93
  "$ref": "#/definitions/property"
90
94
  }
91
95
  }
96
+ },
97
+ "description": {
98
+ "type": "string",
99
+ "description": "Description of the event"
92
100
  }
93
101
  },
94
102
  "required": [
@@ -7,21 +7,25 @@ const { zodResponseFormat } = require('openai/helpers/zod');
7
7
  const openai = new OpenAI({
8
8
  apiKey: process.env.OPENAI_API_KEY,
9
9
  });
10
+ const model = 'gpt-4o-mini';
10
11
 
11
12
  function createPrompt(eventName, properties, implementations, codebaseDir) {
12
- // Initialize the prompt
13
- let prompt = `You are an expert at structured data extraction. Generate detailed descriptions for the following analytics event, its properties, and implementations.\n\n`;
14
-
15
- // Add event name
16
- prompt += `Event Name: "${eventName}"\n\n`;
17
-
18
- // Add properties
13
+ let prompt = `Event Name: "${eventName}"\n\n`;
19
14
  prompt += `Properties:\n`;
20
- for (const propName in properties) {
21
- const prop = properties[propName];
22
- prompt += `- "${propName}" (type: ${prop.type})\n`;
15
+
16
+ function appendPropertiesToPrompt(properties, indent = '') {
17
+ for (const propName in properties) {
18
+ const prop = properties[propName];
19
+ prompt += `${indent}- "${propName}" (type: ${prop.type})\n`;
20
+ if (prop.properties) {
21
+ prompt += `${indent} Sub-properties:\n`;
22
+ appendPropertiesToPrompt(prop.properties, indent + ' ');
23
+ }
24
+ }
23
25
  }
24
26
 
27
+ appendPropertiesToPrompt(properties);
28
+
25
29
  // Add implementations with code snippets
26
30
  prompt += `\nImplementations:\n`;
27
31
  for (const impl of implementations) {
@@ -53,18 +57,39 @@ function getCodeSnippet(filePath, lineNumber, contextLines = 5) {
53
57
  }
54
58
 
55
59
  function createEventDescriptionSchema(properties) {
60
+ function buildPropertySchema(prop) {
61
+ if (prop.properties) {
62
+ const subPropertiesSchema = {};
63
+ for (const subPropName in prop.properties) {
64
+ subPropertiesSchema[subPropName] = buildPropertySchema(prop.properties[subPropName]);
65
+ }
66
+ return z.object({
67
+ description: z
68
+ .string()
69
+ .describe('A maximum of 10 words describing the property and what it means'),
70
+ properties: z.object(subPropertiesSchema),
71
+ });
72
+ } else {
73
+ return z.object({
74
+ description: z
75
+ .string()
76
+ .describe('A maximum of 10 words describing the property and what it means'),
77
+ });
78
+ }
79
+ }
80
+
56
81
  // Define the schema for properties
57
82
  const propertiesSchema = {};
58
83
  for (const propName in properties) {
59
- propertiesSchema[propName] = z.object({
60
- description: z.string().describe('A maximum of 10 words describing the property and what it means'),
61
- });
84
+ propertiesSchema[propName] = buildPropertySchema(properties[propName]);
62
85
  }
63
86
 
64
87
  // Define the schema for implementations
65
88
  const implementationsSchema = z.array(
66
89
  z.object({
67
- description: z.string().describe('A maximum of 10 words describing when this event is triggered'),
90
+ description: z
91
+ .string()
92
+ .describe('A maximum of 10 words describing how this event is triggered without using the word "triggered"'),
68
93
  path: z.string(),
69
94
  line: z.number(),
70
95
  })
@@ -72,7 +97,9 @@ function createEventDescriptionSchema(properties) {
72
97
 
73
98
  // Construct the full schema
74
99
  const eventDescriptionSchema = z.object({
75
- eventDescription: z.string().describe('A maximum of 10 words describing the event and what it describes'),
100
+ eventDescription: z
101
+ .string()
102
+ .describe('A maximum of 10 words describing the event and what it tracks without using the word "tracks"'),
76
103
  properties: z.object(propertiesSchema),
77
104
  implementations: implementationsSchema,
78
105
  });
@@ -83,11 +110,11 @@ function createEventDescriptionSchema(properties) {
83
110
  async function sendPromptToLLM(prompt, schema) {
84
111
  try {
85
112
  const completion = await openai.beta.chat.completions.parse({
86
- model: 'gpt-4o-mini',
113
+ model,
87
114
  messages: [
88
115
  {
89
116
  role: 'system',
90
- content: 'You are an expert at structured data extraction. Generate detailed descriptions for the following analytics event, its properties, and implementations',
117
+ content: 'You are an expert at structured data extraction. Generate detailed descriptions for the following analytics event, its properties, and implementations.',
91
118
  },
92
119
  {
93
120
  role: 'user',
@@ -97,7 +124,10 @@ async function sendPromptToLLM(prompt, schema) {
97
124
  response_format: zodResponseFormat(schema, 'event_description'),
98
125
  });
99
126
 
100
- return completion.choices[0].message.parsed;
127
+ return {
128
+ descriptions: completion.choices[0].message.parsed,
129
+ usage: completion.usage,
130
+ };
101
131
  } catch (error) {
102
132
  console.error('Error during LLM response parsing:', error);
103
133
  return null;
@@ -115,36 +145,58 @@ async function generateEventDescription(eventName, event, codebaseDir) {
115
145
  const eventDescriptionSchema = createEventDescriptionSchema(properties);
116
146
 
117
147
  // Send prompt to the LLM and get the structured response
118
- const descriptions = await sendPromptToLLM(prompt, eventDescriptionSchema);
148
+ const { descriptions, usage } = await sendPromptToLLM(prompt, eventDescriptionSchema);
119
149
 
120
- return { eventName, descriptions };
150
+ return { eventName, descriptions, usage };
121
151
  }
122
152
 
123
153
  async function generateDescriptions(events, codebaseDir) {
154
+ console.log(`Generating descriptions using ${model}`);
155
+
124
156
  const eventPromises = Object.entries(events).map(([eventName, event]) =>
125
157
  generateEventDescription(eventName, event, codebaseDir)
126
158
  );
127
159
 
160
+ console.log(`Running ${eventPromises.length} prompts in parallel...`);
161
+
128
162
  const results = await Promise.all(eventPromises);
129
163
 
164
+ let promptTokens = 0;
165
+ let completionTokens = 0;
166
+
130
167
  // Process results and update the events object
131
- results.forEach(({ eventName, descriptions }) => {
168
+ results.forEach(({ eventName, descriptions, usage }) => {
132
169
  if (descriptions) {
170
+ promptTokens += usage.prompt_tokens;
171
+ completionTokens += usage.completion_tokens;
172
+
133
173
  const event = events[eventName];
134
174
  event.description = descriptions.eventDescription;
135
175
 
136
- // Update property descriptions
137
- for (const propName in descriptions.properties) {
138
- if (event.properties[propName]) {
139
- event.properties[propName].description = descriptions.properties[propName].description;
176
+ // Update property descriptions recursively
177
+ function updatePropertyDescriptions(eventProperties, descriptionProperties) {
178
+ for (const propName in descriptionProperties) {
179
+ if (eventProperties[propName]) {
180
+ eventProperties[propName].description = descriptionProperties[propName].description;
181
+ if (eventProperties[propName].properties && descriptionProperties[propName].properties) {
182
+ updatePropertyDescriptions(
183
+ eventProperties[propName].properties,
184
+ descriptionProperties[propName].properties
185
+ );
186
+ }
187
+ }
140
188
  }
141
189
  }
142
190
 
191
+ updatePropertyDescriptions(event.properties, descriptions.properties);
192
+
143
193
  // Update implementations with descriptions
144
194
  for (let i = 0; i < descriptions.implementations.length; i++) {
145
195
  if (event.implementations[i]) {
146
- if (event.implementations[i].path === descriptions.implementations[i].path &&
147
- event.implementations[i].line === descriptions.implementations[i].line) {
196
+ if (
197
+ event.implementations[i].path === descriptions.implementations[i].path &&
198
+ event.implementations[i].line === descriptions.implementations[i].line
199
+ ) {
148
200
  event.implementations[i].description = descriptions.implementations[i].description;
149
201
  } else {
150
202
  console.error(`Returned implementation description does not match path or line for event: ${eventName}`);
@@ -156,6 +208,10 @@ async function generateDescriptions(events, codebaseDir) {
156
208
  }
157
209
  });
158
210
 
211
+ console.log(`Prompt tokens used: ${promptTokens}`);
212
+ console.log(`Completion tokens used: ${completionTokens}`);
213
+ console.log(`Total tokens used: ${promptTokens + completionTokens}`);
214
+
159
215
  return events;
160
216
  }
161
217