1+ import fs from "fs" ;
2+ import path from "path" ;
3+
4+ // Function to parse agents.ts file and extract agent keys without executing
5+ function parseAgentsFile ( ) : Array < { id : string , agentKeys : string [ ] } > {
6+ const agentsFilePath = path . join ( __dirname , '../src/agents.ts' ) ;
7+ const agentsContent = fs . readFileSync ( agentsFilePath , 'utf8' ) ;
8+
9+ const agentConfigs : Array < { id : string , agentKeys : string [ ] } > = [ ] ;
10+
11+ // Split the content to process each agent configuration individually
12+ const agentBlocks = agentsContent . split ( / (? = \s * { \s * i d : \s * [ " ' ] ) / ) ;
13+
14+ for ( const block of agentBlocks ) {
15+ // Extract the ID
16+ const idMatch = block . match ( / i d : \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / ) ;
17+ if ( ! idMatch ) continue ;
18+
19+ const id = idMatch [ 1 ] ;
20+
21+ // Find the return object by looking for the pattern and then manually parsing balanced braces
22+ const returnMatch = block . match ( / a g e n t s : \s * a s y n c \s * \( \) \s * = > \s * { \s * r e t u r n \s * { / ) ;
23+ if ( ! returnMatch ) continue ;
24+
25+ const startIndex = returnMatch . index ! + returnMatch [ 0 ] . length ;
26+ const returnObjectContent = extractBalancedBraces ( block , startIndex ) ;
27+
28+
29+ // Extract keys from the return object - only capture keys that are followed by a colon and then 'new'
30+ // This ensures we only get the top-level keys like "agentic_chat: new ..." not nested keys like "url: ..."
31+ const keyRegex = / ^ \s * ( \w + ) : \s * n e w \s + \w + / gm;
32+ const keys : string [ ] = [ ] ;
33+ let keyMatch ;
34+ while ( ( keyMatch = keyRegex . exec ( returnObjectContent ) ) !== null ) {
35+ keys . push ( keyMatch [ 1 ] ) ;
36+ }
37+
38+ agentConfigs . push ( { id, agentKeys : keys } ) ;
39+ }
40+
41+ return agentConfigs ;
42+ }
43+
44+ // Helper function to extract content between balanced braces
45+ function extractBalancedBraces ( text : string , startIndex : number ) : string {
46+ let braceCount = 0 ;
47+ let i = startIndex ;
48+
49+ while ( i < text . length ) {
50+ if ( text [ i ] === '{' ) {
51+ braceCount ++ ;
52+ } else if ( text [ i ] === '}' ) {
53+ if ( braceCount === 0 ) {
54+ // Found the closing brace for the return object
55+ return text . substring ( startIndex , i ) ;
56+ }
57+ braceCount -- ;
58+ }
59+ i ++ ;
60+ }
61+
62+ return '' ;
63+ }
64+
65+ const agentConfigs = parseAgentsFile ( ) ;
66+ console . log ( "Loaded agents:" , agentConfigs . length ) ;
67+
68+ const featureFiles = [ "page.tsx" , "style.css" , "README.mdx" ]
69+
70+ function getFile ( _filePath : string | undefined , _fileName ?: string ) {
71+ if ( ! _filePath ) {
72+ console . warn ( `File path is undefined, skipping.` ) ;
73+ return { }
74+ }
75+ const fileName = _fileName ?? _filePath . split ( '/' ) . pop ( ) ?? ''
76+ const filePath = _fileName ? path . join ( _filePath , fileName ) : _filePath ;
77+ if ( ! fs . existsSync ( filePath ) ) {
78+ console . warn ( `File not found: ${ filePath } , skipping.` ) ;
79+ return { }
80+ }
81+
82+ try {
83+ const content = fs . readFileSync ( filePath , "utf8" ) ;
84+ const extension = fileName . split ( "." ) . pop ( ) ;
85+ let language = extension ;
86+ if ( extension === "py" ) language = "python" ;
87+ else if ( extension === "css" ) language = "css" ;
88+ else if ( extension === "md" || extension === "mdx" ) language = "markdown" ;
89+ else if ( extension === "tsx" ) language = "typescript" ;
90+ else if ( extension === "js" ) language = "javascript" ;
91+ else if ( extension === "json" ) language = "json" ;
92+ else if ( extension === "yaml" || extension === "yml" ) language = "yaml" ;
93+ else if ( extension === "toml" ) language = "toml" ;
94+
95+ return {
96+ name : fileName ,
97+ content,
98+ // path: path.join(demoIdWithFramework, fileName), // Store relative path within agent/demo
99+ language,
100+ type : 'file'
101+ }
102+ } catch ( error ) {
103+ console . error ( `Error reading file ${ filePath } :` , error ) ;
104+ }
105+ }
106+
107+ function getFeatureFrontendFiles ( featureId : string ) {
108+ const featurePath = path . join ( __dirname , `../src/app/[integrationId]/feature/${ featureId as string } ` ) ;
109+ const retrievedFiles = [ ]
110+
111+ for ( const fileName of featureFiles ) {
112+ retrievedFiles . push ( getFile ( featurePath , fileName ) )
113+ }
114+
115+ return retrievedFiles ;
116+ }
117+
118+ const integrationsFolderPath = '../../../integrations'
119+ const agentFilesMapper : Record < string , ( agentKeys : string [ ] ) => Record < string , string > > = {
120+ 'middleware-starter' : ( ) => ( {
121+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/middleware-starter/src/index.ts` )
122+ } ) ,
123+ 'pydantic-ai' : ( agentKeys : string [ ] ) => {
124+ return agentKeys . reduce ( ( acc , agentId ) => ( {
125+ ...acc ,
126+ [ agentId ] : `https://github.com/pydantic/pydantic-ai/blob/main/examples/pydantic_ai_examples/ag_ui/api/${ agentId } .py`
127+ } ) , { } )
128+ } ,
129+ 'server-starter' : ( ) => ( {
130+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/server-starter/server/python/example_server/__init__.py` )
131+ } ) ,
132+ 'server-starter-all-features' : ( agentKeys : string [ ] ) => {
133+ return agentKeys . reduce ( ( acc , agentId ) => ( {
134+ ...acc ,
135+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/server-starter/server/python/example_server/${ agentId } .py` )
136+ } ) , { } )
137+ } ,
138+ 'mastra' : ( ) => ( {
139+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/mastra/example/src/mastra/agents/weather-agent.ts` )
140+ } ) ,
141+ 'mastra-agent-lock' : ( ) => ( {
142+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/mastra/example/src/mastra/agents/weather-agent.ts` )
143+ } ) ,
144+ 'vercel-ai-sdk' : ( ) => ( {
145+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/vercel-ai-sdk/src/index.ts` )
146+ } ) ,
147+ 'langgraph' : ( agentKeys : string [ ] ) => {
148+ return agentKeys . reduce ( ( acc , agentId ) => ( {
149+ ...acc ,
150+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/langgraph/examples/agents/${ agentId } /agent.py` )
151+ } ) , { } )
152+ } ,
153+ 'langgraph-fastapi' : ( agentKeys : string [ ] ) => {
154+ return agentKeys . reduce ( ( acc , agentId ) => ( {
155+ ...acc ,
156+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/langgraph/python/ag_ui_langgraph/examples/agents/${ agentId } .py` )
157+ } ) , { } )
158+ } ,
159+ 'agno' : ( ) => ( { } ) ,
160+ 'llama-index' : ( agentKeys : string [ ] ) => {
161+ return agentKeys . reduce ( ( acc , agentId ) => ( {
162+ ...acc ,
163+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/llamaindex/server-py/server/routers/${ agentId } .py` )
164+ } ) , { } )
165+ } ,
166+ 'crewai' : ( agentKeys : string [ ] ) => {
167+ return agentKeys . reduce ( ( acc , agentId ) => ( {
168+ ...acc ,
169+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/crewai/python/ag_ui_crewai/examples/${ agentId } .py` )
170+ } ) , { } )
171+ }
172+ }
173+
174+ function runGenerateContent ( ) {
175+ const result = { }
176+ for ( const agentConfig of agentConfigs ) {
177+ // Use the parsed agent keys instead of executing the agents function
178+ const agentsPerFeatures = agentConfig . agentKeys
179+
180+ const agentFilePaths = agentFilesMapper [ agentConfig . id ] ( agentConfig . agentKeys )
181+ // Per feature, assign all the frontend files like page.tsx as well as all agent files
182+ agentsPerFeatures . forEach ( featureId => {
183+ // @ts -expect-error -- redundant error about indexing of a new object.
184+ result [ `${ agentConfig . id } ::${ featureId } ` ] = [
185+ // Get all frontend files for the feature
186+ ...getFeatureFrontendFiles ( featureId ) ,
187+ // Get the agent (python/TS) file
188+ getFile ( agentFilePaths [ featureId ] )
189+ ]
190+ } )
191+ }
192+
193+ return result
194+ }
195+
196+ // const result = {};
197+ // const agentDemoBaseDir = path.join(__dirname, "../agent/demo");
198+ //
199+ // for (const demoIdWithFramework in config) {
200+ // const demoFilesConfig = config[demoIdWithFramework];
201+ // const demoDirPath = path.join(agentDemoBaseDir, demoIdWithFramework);
202+ //
203+ // if (!fs.existsSync(demoDirPath)) {
204+ // console.warn(`Directory not found for demo: ${demoIdWithFramework}, skipping.`);
205+ // continue;
206+ // }
207+ //
208+ // result[demoIdWithFramework] = { files: [] };
209+ //
210+ // for (const fileName of demoFilesConfig) {
211+ // const filePath = path.join(demoDirPath, fileName);
212+ // if (!fs.existsSync(filePath)) {
213+ // console.warn(`File not found: ${filePath}, skipping.`);
214+ // continue;
215+ // }
216+ //
217+ // try {
218+ // const content = fs.readFileSync(filePath, "utf8");
219+ // const extension = fileName.split(".").pop();
220+ // let language = extension;
221+ // if (extension === "py") language = "python";
222+ // else if (extension === "css") language = "css";
223+ // else if (extension === "md" || extension === "mdx") language = "markdown";
224+ // else if (extension === "tsx") language = "typescript";
225+ // else if (extension === "js") language = "javascript";
226+ // else if (extension === "json") language = "json";
227+ // else if (extension === "yaml" || extension === "yml") language = "yaml";
228+ // else if (extension === "toml") language = "toml";
229+ //
230+ // result[demoIdWithFramework].files.push({
231+ // name: fileName,
232+ // content,
233+ // path: path.join(demoIdWithFramework, fileName), // Store relative path within agent/demo
234+ // language,
235+ // type: 'file'
236+ // });
237+ // } catch (error) {
238+ // console.error(`Error reading file ${filePath}:`, error);
239+ // }
240+ // }
241+ // }
242+ const result = runGenerateContent ( ) ;
243+ fs . writeFileSync (
244+ path . join ( __dirname , "../src/files.json" ) ,
245+ JSON . stringify ( result , null , 2 )
246+ ) ;
247+
248+ console . log ( "Successfully generated src/files.json" ) ;
0 commit comments