@nxcode/sdk 1.0.5 → 1.0.12
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/dist/nxcode.esm.js +298 -1
- package/dist/nxcode.esm.js.map +1 -1
- package/dist/nxcode.js +298 -0
- package/dist/nxcode.js.map +1 -1
- package/dist/nxcode.min.js +1 -1
- package/dist/types/agent.d.ts +102 -0
- package/dist/types/index.d.ts +30 -2
- package/package.json +1 -1
package/dist/nxcode.esm.js
CHANGED
|
@@ -707,6 +707,272 @@ class NxcodeAI {
|
|
|
707
707
|
}
|
|
708
708
|
}
|
|
709
709
|
|
|
710
|
+
/**
|
|
711
|
+
* Nxcode SDK - Agent Module
|
|
712
|
+
*
|
|
713
|
+
* Provides AI agents with tool calling capabilities.
|
|
714
|
+
* Agents can call backend endpoints as tools and maintain conversation history.
|
|
715
|
+
*/
|
|
716
|
+
// ==================== Helper Functions ====================
|
|
717
|
+
/**
|
|
718
|
+
* Convert simple schema to JSON Schema format for Gemini
|
|
719
|
+
*/
|
|
720
|
+
function toJsonSchemaProperty(schema) {
|
|
721
|
+
if (schema === 'string')
|
|
722
|
+
return { type: 'STRING' };
|
|
723
|
+
if (schema === 'number')
|
|
724
|
+
return { type: 'NUMBER' };
|
|
725
|
+
if (schema === 'boolean')
|
|
726
|
+
return { type: 'BOOLEAN' };
|
|
727
|
+
if (Array.isArray(schema)) {
|
|
728
|
+
return {
|
|
729
|
+
type: 'ARRAY',
|
|
730
|
+
items: toJsonSchemaProperty(schema[0]),
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
if (typeof schema === 'object') {
|
|
734
|
+
const properties = {};
|
|
735
|
+
const required = [];
|
|
736
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
737
|
+
properties[key] = toJsonSchemaProperty(value);
|
|
738
|
+
required.push(key);
|
|
739
|
+
}
|
|
740
|
+
return { type: 'OBJECT', properties, required };
|
|
741
|
+
}
|
|
742
|
+
return { type: 'STRING' };
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Convert AgentTool to Gemini function declaration
|
|
746
|
+
*/
|
|
747
|
+
function toolToGeminiFunction(tool) {
|
|
748
|
+
const declaration = {
|
|
749
|
+
name: tool.name,
|
|
750
|
+
description: tool.description,
|
|
751
|
+
};
|
|
752
|
+
// Always include parameters object (even if empty) for Gemini compatibility
|
|
753
|
+
const properties = {};
|
|
754
|
+
const required = [];
|
|
755
|
+
if (tool.parameters) {
|
|
756
|
+
for (const [key, value] of Object.entries(tool.parameters)) {
|
|
757
|
+
properties[key] = toJsonSchemaProperty(value);
|
|
758
|
+
required.push(key);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
declaration.parameters = {
|
|
762
|
+
type: 'OBJECT',
|
|
763
|
+
properties,
|
|
764
|
+
required,
|
|
765
|
+
};
|
|
766
|
+
return declaration;
|
|
767
|
+
}
|
|
768
|
+
// ==================== Agent Class ====================
|
|
769
|
+
class Agent {
|
|
770
|
+
constructor(options, apiEndpoint, appId, getToken) {
|
|
771
|
+
this.history = [];
|
|
772
|
+
this.options = {
|
|
773
|
+
instructions: options.instructions,
|
|
774
|
+
tools: options.tools,
|
|
775
|
+
model: options.model || 'fast',
|
|
776
|
+
maxSteps: options.maxSteps || 10,
|
|
777
|
+
};
|
|
778
|
+
this.apiEndpoint = apiEndpoint;
|
|
779
|
+
this.appId = appId;
|
|
780
|
+
this.getToken = getToken;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Run the agent with user input
|
|
784
|
+
*/
|
|
785
|
+
async run(input, callbacks) {
|
|
786
|
+
const token = this.getToken();
|
|
787
|
+
if (!token) {
|
|
788
|
+
throw new Error('Not authenticated. Please login first.');
|
|
789
|
+
}
|
|
790
|
+
// Add user message to history
|
|
791
|
+
this.history.push({ role: 'user', content: input });
|
|
792
|
+
const toolCalls = [];
|
|
793
|
+
let totalInputTokens = 0;
|
|
794
|
+
let totalOutputTokens = 0;
|
|
795
|
+
let steps = 0;
|
|
796
|
+
// Agent loop
|
|
797
|
+
while (steps < this.options.maxSteps) {
|
|
798
|
+
steps++;
|
|
799
|
+
console.log(`[Agent] Step ${steps}/${this.options.maxSteps}`);
|
|
800
|
+
// Call Gemini with tools
|
|
801
|
+
const response = await this.callGemini(token);
|
|
802
|
+
console.log(`[Agent] Response:`, response.functionCall ? `function call: ${response.functionCall.name}` : `text: ${response.text?.substring(0, 50)}`);
|
|
803
|
+
totalInputTokens += response.usage?.inputTokens || 0;
|
|
804
|
+
totalOutputTokens += response.usage?.outputTokens || 0;
|
|
805
|
+
// Check if AI wants to call a function
|
|
806
|
+
const functionCall = response.functionCall;
|
|
807
|
+
if (functionCall) {
|
|
808
|
+
callbacks?.onToolCall?.(functionCall.name, functionCall.args);
|
|
809
|
+
// Find the tool
|
|
810
|
+
const tool = this.options.tools.find((t) => t.name === functionCall.name);
|
|
811
|
+
if (!tool) {
|
|
812
|
+
throw new Error(`Unknown tool: ${functionCall.name}`);
|
|
813
|
+
}
|
|
814
|
+
// Call the tool endpoint
|
|
815
|
+
const result = await this.callTool(tool, functionCall.args, token);
|
|
816
|
+
callbacks?.onToolResult?.(functionCall.name, result);
|
|
817
|
+
// Record tool call
|
|
818
|
+
toolCalls.push({
|
|
819
|
+
name: functionCall.name,
|
|
820
|
+
params: functionCall.args,
|
|
821
|
+
result,
|
|
822
|
+
});
|
|
823
|
+
// Add function call and response to history using raw parts from Gemini
|
|
824
|
+
this.history.push({
|
|
825
|
+
role: 'model',
|
|
826
|
+
content: '',
|
|
827
|
+
parts: response.rawParts || [{ functionCall: { name: functionCall.name, args: functionCall.args } }],
|
|
828
|
+
});
|
|
829
|
+
this.history.push({
|
|
830
|
+
role: 'user',
|
|
831
|
+
content: '',
|
|
832
|
+
parts: [{ functionResponse: { name: functionCall.name, response: result } }],
|
|
833
|
+
});
|
|
834
|
+
// Continue loop to let AI process the result
|
|
835
|
+
console.log(`[Agent] Continuing loop after tool call...`);
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
// No function call - AI is done, return the text response
|
|
839
|
+
const output = response.text || '';
|
|
840
|
+
// Add assistant response to history
|
|
841
|
+
this.history.push({ role: 'assistant', content: output });
|
|
842
|
+
return {
|
|
843
|
+
output,
|
|
844
|
+
toolCalls,
|
|
845
|
+
usage: {
|
|
846
|
+
inputTokens: totalInputTokens,
|
|
847
|
+
outputTokens: totalOutputTokens,
|
|
848
|
+
},
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
throw new Error(`Agent exceeded maximum steps (${this.options.maxSteps})`);
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Clear conversation history
|
|
855
|
+
*/
|
|
856
|
+
reset() {
|
|
857
|
+
this.history = [];
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Get current conversation history
|
|
861
|
+
*/
|
|
862
|
+
getHistory() {
|
|
863
|
+
return [...this.history];
|
|
864
|
+
}
|
|
865
|
+
// ==================== Private Methods ====================
|
|
866
|
+
async callGemini(token) {
|
|
867
|
+
// Build contents from history
|
|
868
|
+
const contents = this.history.map((msg) => {
|
|
869
|
+
if (msg.parts) {
|
|
870
|
+
return {
|
|
871
|
+
role: msg.role === 'assistant' ? 'model' : msg.role,
|
|
872
|
+
parts: msg.parts,
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
role: msg.role === 'assistant' ? 'model' : 'user',
|
|
877
|
+
parts: [{ text: msg.content }],
|
|
878
|
+
};
|
|
879
|
+
});
|
|
880
|
+
// Build tools
|
|
881
|
+
const tools = [
|
|
882
|
+
{
|
|
883
|
+
functionDeclarations: this.options.tools.map(toolToGeminiFunction),
|
|
884
|
+
},
|
|
885
|
+
];
|
|
886
|
+
// Build request with tool config to encourage tool use
|
|
887
|
+
const request = {
|
|
888
|
+
contents,
|
|
889
|
+
tools,
|
|
890
|
+
toolConfig: {
|
|
891
|
+
functionCallingConfig: {
|
|
892
|
+
mode: 'AUTO', // Let model decide when to use tools
|
|
893
|
+
},
|
|
894
|
+
},
|
|
895
|
+
systemInstruction: {
|
|
896
|
+
parts: [{ text: this.options.instructions }],
|
|
897
|
+
},
|
|
898
|
+
};
|
|
899
|
+
console.log('[Agent] Sending request:', JSON.stringify(request, null, 2));
|
|
900
|
+
const response = await fetch(`${this.apiEndpoint}/api/ai-gateway/v1beta/models/${this.options.model}:generateContent`, {
|
|
901
|
+
method: 'POST',
|
|
902
|
+
headers: {
|
|
903
|
+
'Content-Type': 'application/json',
|
|
904
|
+
'X-App-Id': this.appId,
|
|
905
|
+
Authorization: `Bearer ${token}`,
|
|
906
|
+
},
|
|
907
|
+
body: JSON.stringify(request),
|
|
908
|
+
});
|
|
909
|
+
if (!response.ok) {
|
|
910
|
+
const error = await response.json().catch(() => ({}));
|
|
911
|
+
throw new Error(error.detail || error.message || 'Agent API call failed');
|
|
912
|
+
}
|
|
913
|
+
const data = await response.json();
|
|
914
|
+
// Parse response
|
|
915
|
+
const candidate = data.candidates?.[0];
|
|
916
|
+
const parts = candidate?.content?.parts || [];
|
|
917
|
+
// Check for function call (support both camelCase and snake_case)
|
|
918
|
+
for (const part of parts) {
|
|
919
|
+
const funcCall = part.functionCall || part.function_call;
|
|
920
|
+
if (funcCall) {
|
|
921
|
+
return {
|
|
922
|
+
functionCall: {
|
|
923
|
+
name: funcCall.name,
|
|
924
|
+
args: funcCall.args || {},
|
|
925
|
+
},
|
|
926
|
+
rawParts: parts, // Save raw parts including thought_signature
|
|
927
|
+
usage: {
|
|
928
|
+
inputTokens: data.usageMetadata?.promptTokenCount || 0,
|
|
929
|
+
outputTokens: data.usageMetadata?.candidatesTokenCount || 0,
|
|
930
|
+
},
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
// Return text response
|
|
935
|
+
const text = parts.find((p) => p.text)?.text || '';
|
|
936
|
+
return {
|
|
937
|
+
text,
|
|
938
|
+
usage: {
|
|
939
|
+
inputTokens: data.usageMetadata?.promptTokenCount || 0,
|
|
940
|
+
outputTokens: data.usageMetadata?.candidatesTokenCount || 0,
|
|
941
|
+
},
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
async callTool(tool, params, token) {
|
|
945
|
+
// Build context to pass to tool
|
|
946
|
+
const body = {
|
|
947
|
+
...params,
|
|
948
|
+
_context: {
|
|
949
|
+
agentId: this.options.instructions.slice(0, 20), // Simple ID
|
|
950
|
+
stepIndex: this.history.length,
|
|
951
|
+
previousTools: this.history
|
|
952
|
+
.filter((m) => m.parts?.some((p) => p.functionResponse))
|
|
953
|
+
.map((m) => {
|
|
954
|
+
const fr = m.parts?.find((p) => p.functionResponse)?.functionResponse;
|
|
955
|
+
return fr ? { name: fr.name, result: fr.response } : null;
|
|
956
|
+
})
|
|
957
|
+
.filter(Boolean),
|
|
958
|
+
},
|
|
959
|
+
};
|
|
960
|
+
const response = await fetch(tool.endpoint, {
|
|
961
|
+
method: 'POST',
|
|
962
|
+
headers: {
|
|
963
|
+
'Content-Type': 'application/json',
|
|
964
|
+
Authorization: `Bearer ${token}`,
|
|
965
|
+
},
|
|
966
|
+
body: JSON.stringify(body),
|
|
967
|
+
});
|
|
968
|
+
if (!response.ok) {
|
|
969
|
+
const error = await response.text();
|
|
970
|
+
return { error: `Tool call failed: ${error}` };
|
|
971
|
+
}
|
|
972
|
+
return response.json();
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
710
976
|
/**
|
|
711
977
|
* Nxcode SDK
|
|
712
978
|
*
|
|
@@ -971,6 +1237,37 @@ class NxcodeSDK {
|
|
|
971
1237
|
await self.ensureInitialized();
|
|
972
1238
|
return self._ai.generateStream(options);
|
|
973
1239
|
},
|
|
1240
|
+
/**
|
|
1241
|
+
* Create an AI agent with tool calling capabilities
|
|
1242
|
+
*
|
|
1243
|
+
* @example
|
|
1244
|
+
* const agent = Nxcode.ai.createAgent({
|
|
1245
|
+
* instructions: 'You are a helpful assistant that can search and send emails.',
|
|
1246
|
+
* tools: [
|
|
1247
|
+
* {
|
|
1248
|
+
* name: 'search_emails',
|
|
1249
|
+
* description: 'Search user emails',
|
|
1250
|
+
* parameters: { query: 'string' },
|
|
1251
|
+
* endpoint: '/api/emails/search'
|
|
1252
|
+
* },
|
|
1253
|
+
* {
|
|
1254
|
+
* name: 'send_email',
|
|
1255
|
+
* description: 'Send an email',
|
|
1256
|
+
* parameters: { to: 'string', subject: 'string', body: 'string' },
|
|
1257
|
+
* endpoint: '/api/emails/send'
|
|
1258
|
+
* }
|
|
1259
|
+
* ]
|
|
1260
|
+
* });
|
|
1261
|
+
*
|
|
1262
|
+
* const result = await agent.run('Find emails from John and reply to him');
|
|
1263
|
+
* console.log(result.output);
|
|
1264
|
+
*/
|
|
1265
|
+
createAgent(options) {
|
|
1266
|
+
if (!self.config) {
|
|
1267
|
+
throw new Error('Nxcode SDK not configured. Call Nxcode.configure(appId) first.');
|
|
1268
|
+
}
|
|
1269
|
+
return new Agent(options, self.apiEndpoint, self.config.appId, () => self._auth?.getToken() || null);
|
|
1270
|
+
},
|
|
974
1271
|
};
|
|
975
1272
|
}
|
|
976
1273
|
// ==================== Utility Methods ====================
|
|
@@ -1000,5 +1297,5 @@ if (typeof window !== 'undefined') {
|
|
|
1000
1297
|
window.Nxcode = Nxcode;
|
|
1001
1298
|
}
|
|
1002
1299
|
|
|
1003
|
-
export { Nxcode, Nxcode as default };
|
|
1300
|
+
export { Agent, Nxcode, Nxcode as default };
|
|
1004
1301
|
//# sourceMappingURL=nxcode.esm.js.map
|