-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate the pointer array configuration through gpt (#63)
Try using gpt as a renderer to help us alleviate some manual work.
- Loading branch information
1 parent
5b27958
commit b895d4f
Showing
8 changed files
with
628 additions
and
2 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
import { CXXTYPE } from '@agoraio-extensions/cxx-parser'; | ||
|
||
module.exports = { | ||
markers: [ | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'LiveTranscoding', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'transcodingUsers', | ||
lengthName: 'userCount', | ||
}, | ||
{ | ||
ptrName: 'watermark', | ||
lengthName: 'watermarkCount', | ||
}, | ||
{ | ||
ptrName: 'backgroundImage', | ||
lengthName: 'backgroundImageCount', | ||
}, | ||
{ | ||
ptrName: 'advancedFeatures', | ||
lengthName: 'advancedFeatureCount', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'ScreenCaptureParameters', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'excludeWindowList', | ||
lengthName: 'excludeWindowCount', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'ChannelMediaRelayConfiguration', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'destInfos', | ||
lengthName: 'destCount', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'LocalAccessPointConfiguration', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'ipList', | ||
lengthName: 'ipListSize', | ||
}, | ||
{ | ||
ptrName: 'domainList', | ||
lengthName: 'domainListSize', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'ContentInspectConfig', | ||
namespaces: ['agora', 'media'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'modules', | ||
lengthName: 'moduleCount', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'AudioSpectrumData', | ||
namespaces: ['agora', 'media'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'audioSpectrumData', | ||
lengthName: 'dataLength', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'LogConfig', | ||
namespaces: ['agora', 'commons'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'filePath', | ||
lengthName: 'fileSizeInKB', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'InputSeiData', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'private_data', | ||
lengthName: 'data_size', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'Music', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'lyricList', | ||
lengthName: 'lyricCount', | ||
}, | ||
{ | ||
ptrName: 'climaxSegmentList', | ||
lengthName: 'climaxSegmentCount', | ||
}, | ||
{ | ||
ptrName: 'mvPropertyList', | ||
lengthName: 'mvPropertyCount', | ||
}, | ||
], | ||
}, | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: 'VideoCompositingLayout', | ||
namespaces: ['agora', 'rtc'], | ||
}, | ||
pointerArrayNameMappings: [ | ||
{ | ||
ptrName: 'regions', | ||
lengthName: 'regionCount', | ||
}, | ||
{ | ||
ptrName: 'appData', | ||
lengthName: 'appDataLength', | ||
}, | ||
], | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,10 +24,13 @@ | |
"dependencies": { | ||
"@agoraio-extensions/cxx-parser": "[email protected]:AgoraIO-Extensions/terra.git#head=main&workspace=cxx-parser", | ||
"@agoraio-extensions/terra-core": "[email protected]:AgoraIO-Extensions/terra.git#head=main&workspace=terra-core", | ||
"mustache": "^4.2.0" | ||
"lodash": "^4.17.21", | ||
"mustache": "^4.2.0", | ||
"openai": "^4.29.1" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^29.5.1", | ||
"@types/lodash": "^4.17.0", | ||
"@types/mustache": "^4.2.2", | ||
"@types/node": "^20.5.9", | ||
"@typescript-eslint/eslint-plugin": "^5.30.5", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export * from './renderers/mustache_renderer'; | ||
export * from './renderers/iris_doc_renderer'; | ||
export * from './renderers/pointer_marker_gpt_renderer'; | ||
|
||
export * from './parsers'; | ||
export * from './utils'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
import { execSync } from 'child_process'; | ||
import * as fs from 'fs'; | ||
import path from 'path'; | ||
import _ from 'lodash'; | ||
|
||
import { | ||
ParseResult, | ||
RenderResult, | ||
TerraContext, | ||
} from '@agoraio-extensions/terra-core'; | ||
import { askGPT } from '../utils/gpt_utils'; | ||
import { PointerArrayNameMapping, PointerMarkerParserConfigMarker } from '../parsers/pointer_marker_parser'; | ||
import { CXXFile, SimpleTypeKind, Struct } from '@agoraio-extensions/cxx-parser'; | ||
|
||
export interface PointerMarkerGPTRenderer { | ||
configPath?: string | ||
} | ||
|
||
export function PointerMarkerGPTRenderer( | ||
terraContext: TerraContext, | ||
args?: PointerMarkerGPTRenderer, | ||
parseResult?: ParseResult | ||
): RenderResult[] { | ||
processGPT(parseResult!, args?.configPath); | ||
return []; | ||
} | ||
|
||
const prompt = ` | ||
You are a C++ Code Inspector. Your task is to identify "pointer field-length field" pairs within a given C++ struct. | ||
Exclude any pair where the pointer field's name is "buffer" or the pointer field type is "uint8_t" pointer. Additionally, disregard any pointer without a direct length field counterpart. | ||
Your output should be in JSON format, returning an empty array [] if no valid pairs are found, or [{"ptrName": "pointer_name", "lengthName": "length_field_name"}] for each valid pair found. | ||
The response MUST contains only the JSON result without any additional explanations. | ||
Given struct: | ||
\`\`\`c++ | ||
{{ STRUCT_SOURCE }} | ||
\`\`\` | ||
`; | ||
|
||
const defualtConfigPath = path.resolve(`${__dirname}/../../configs/rtc/pointer_marker.config.ts`); | ||
async function processGPT(parseResult: ParseResult, configPath?: string) { | ||
let originalConfigPath = configPath ?? defualtConfigPath; | ||
let originalMarkers = require(originalConfigPath).markers as PointerMarkerParserConfigMarker[]; | ||
|
||
let structs = parseResult.nodes | ||
.flatMap((node) => (node as CXXFile).nodes) | ||
.filter((node) => node.isStruct()) | ||
.filter((node) => { | ||
return node.asStruct() | ||
.member_variables | ||
.find((member) => member.type.kind === SimpleTypeKind.pointer_t) !== undefined; | ||
}); | ||
|
||
let markers: string[] = []; | ||
|
||
for (let st of structs) { | ||
let struct = st.asStruct(); | ||
let structSource = structToSource(struct); | ||
let promptWithStruct = prompt | ||
.replace('{{ STRUCT_NAME }}', struct.name) | ||
.replace('{{ STRUCT_SOURCE }}', structSource) | ||
.trim(); | ||
let res = await askGPT(promptWithStruct); | ||
if (res.length > 0) { | ||
let jsonArray = Object.values(JSON.parse(res)); | ||
if (jsonArray.length === 0) { | ||
continue; | ||
} | ||
|
||
// let newJsonArray = jsonArray as PointerArrayNameMapping[]; | ||
let newJsonArray: PointerArrayNameMapping[] = []; | ||
let originalMarker = originalMarkers.find((entry: any) => _.isMatch(struct, entry.node)); | ||
if (originalMarker) { | ||
for (let om of (jsonArray as PointerArrayNameMapping[])) { | ||
if (om.lengthName.length === 0) { | ||
continue; | ||
} | ||
let found = originalMarker.pointerArrayNameMappings?.find((entry) => entry.ptrName === om.ptrName); | ||
// Only add the ptrName if it's not found in the original marker | ||
let toAdd = found ? found : om; | ||
newJsonArray.push(toAdd); | ||
} | ||
} | ||
|
||
if (newJsonArray.length > 0) { | ||
let pointerArrayNameMappings = newJsonArray.map((entry: any) => { | ||
return ` | ||
{ | ||
ptrName: "${entry.ptrName}", | ||
lengthName: "${entry.lengthName}", | ||
}`.trim(); | ||
}); | ||
|
||
let marker = ` | ||
{ | ||
node: { | ||
__TYPE: CXXTYPE.Struct, | ||
name: "${struct.name}", | ||
namespaces: [${struct.namespaces.map((it) => `"${it}"`).join(',')}], | ||
}, | ||
pointerArrayNameMappings: [ | ||
${pointerArrayNameMappings.join(',\n')} | ||
], | ||
}`.trim(); | ||
markers.push(marker); | ||
} | ||
} | ||
} | ||
console.log(markers); | ||
|
||
let output = ` | ||
import { CXXTYPE } from "@agoraio-extensions/cxx-parser"; | ||
module.exports = { | ||
markers: [ | ||
${markers.join(',\n')} | ||
], | ||
}; | ||
`.trim(); | ||
|
||
fs.writeFileSync(originalConfigPath, output); | ||
|
||
// Reformat the file | ||
execSync(`yarn prettier ${originalConfigPath} --write`, { | ||
cwd: path.resolve(__dirname, '../../'), | ||
}); | ||
} | ||
|
||
function structToSource(struct: Struct): string { | ||
let structName = struct.name; | ||
let structContent = struct.member_variables.map((member) => { | ||
let memberName = member.name; | ||
let memberType = member.type.source; | ||
let memberComment = member.comment.split('\n').map((line) => `/// ${line}`).join('\n'); | ||
return ` | ||
${memberComment} | ||
${memberType} ${memberName};`.trim(); | ||
}).join('\n\n'); | ||
|
||
return ` | ||
struct ${structName} { | ||
${structContent} | ||
};`.trim(); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import OpenAI from "openai"; | ||
|
||
let _openAIClient: OpenAI | undefined = undefined; | ||
function openAIClient(): OpenAI { | ||
if (_openAIClient === undefined) { | ||
_openAIClient = new OpenAI(); | ||
} | ||
return _openAIClient; | ||
} | ||
|
||
/// Make sure you add the following environment variables before you call this function | ||
/// - OPENAI_API_KEY | ||
/// - OPENAI_BASE_URL | ||
export async function askGPT(prompt: string): Promise<string> { | ||
console.log(`prompt:`); | ||
console.log(prompt); | ||
|
||
const completion: OpenAI.Chat.ChatCompletion = (await openAIClient().chat.completions.create({ | ||
model: 'gpt-4', | ||
messages: [{ role: 'user', content: prompt }], | ||
temperature: 0.7, | ||
})); | ||
|
||
let response: any | undefined = undefined; | ||
try { | ||
// We can only make a synchronous call inside terra at this time, but in this way the completions API returns a string, so we need to | ||
// do some tricky thing here. | ||
if (completion !== undefined && typeof completion === 'string') { | ||
let completionStr = completion as string; | ||
if (completionStr.length > 0) { | ||
response = JSON.parse(completionStr); | ||
} | ||
} else if (typeof completion === 'object' && completion !== null) { | ||
response = completion; | ||
} else { | ||
console.log('Param is neither a string nor an object'); | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
|
||
let res = response?.choices[0]?.message?.content ?? ''; | ||
|
||
console.log(`response:`); | ||
console.log(res); | ||
|
||
return res; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.