From c1246b538d84b65fe3b3a0fc564e98f5d6e041e4 Mon Sep 17 00:00:00 2001 From: Manas Hejmadi Date: Sun, 6 Apr 2025 22:38:09 +0530 Subject: [PATCH] Consolidator & Tool Templates Done --- lib/apitoolgen/request_consolidator.dart | 97 +++++++++ lib/apitoolgen/tool_templates.dart | 93 +++++++++ .../agentic_services/agents/agents.dart | 1 + .../agents/apitool_funcgen.dart | 47 +++++ lib/widgets/response_pane_header.dart | 193 +++++++++++++----- 5 files changed, 378 insertions(+), 53 deletions(-) create mode 100644 lib/apitoolgen/request_consolidator.dart create mode 100644 lib/apitoolgen/tool_templates.dart create mode 100644 lib/services/agentic_services/agents/apitool_funcgen.dart diff --git a/lib/apitoolgen/request_consolidator.dart b/lib/apitoolgen/request_consolidator.dart new file mode 100644 index 00000000..fe8c4d5f --- /dev/null +++ b/lib/apitoolgen/request_consolidator.dart @@ -0,0 +1,97 @@ +class APIDashRequestDescription { + final String endpoint; + final String method; + final Map? queryParams; + final List? formData; + final Map? headers; + final String? bodyTXT; + final Map? bodyJSON; + final String? responseType; + final dynamic response; + + APIDashRequestDescription({ + required this.endpoint, + required this.method, + this.queryParams, + this.formData, + this.headers, + this.bodyTXT, + this.bodyJSON, + this.responseType, + this.response, + }); + + String get generateREQDATA { + //Note Down the Query parameters + String queryParamStr = ''; + if (queryParams != null) { + for (final x in queryParams!.keys) { + queryParamStr += + '\t$x: ${queryParams![x]} <${queryParams![x].runtimeType}>\n'; + } + queryParamStr = 'QUERY_PARAMETERS: {\n$queryParamStr}'; + } + + //Note Down the Headers + String headersStr = ''; + if (headers != null) { + for (final x in headers!.keys) { + headersStr += '\t$x: ${headers![x]} <${headers![x].runtimeType}>\n'; + } + headersStr = 'HEADERS: {\n$headersStr}'; + } + + String bodyDetails = ''; + if (bodyTXT != null) { + bodyDetails = 'BODY_TYPE: TEXT\nBODY_TEXT:$bodyTXT'; + } else if (bodyJSON != null) { + //Note Down the JSONData + String jsonBodyStr = ''; + if (bodyJSON != null) { + getTyp(input, [i = 0]) { + String indent = "\t"; + for (int j = 0; j < i; j++) indent += "\t"; + if (input.runtimeType.toString().toLowerCase().contains('map')) { + String typd = '{'; + for (final z in input.keys) { + typd += "$indent$z: TYPE: ${getTyp(input[z], i + 1)}\n"; + } + return "$indent$typd}"; + } + return input.runtimeType.toString(); + } + + for (final x in bodyJSON!.keys) { + jsonBodyStr += '\t$x: TYPE: <${getTyp(bodyJSON![x])}>\n'; + } + jsonBodyStr = 'BODY_JSON: {\n$jsonBodyStr}'; + } + bodyDetails = 'BODY_TYPE: JSON\n$jsonBodyStr'; + } else if (formData != null) { + //Note Down the FormData + String formDataStr = ''; + if (formData != null) { + for (final x in formData!) { + formDataStr += '\t$x\n'; + } + formDataStr = 'BODY_FORM_DATA: {\n$formDataStr}'; + } + bodyDetails = 'BODY_TYPE: FORM-DATA\n$formDataStr'; + } + + String responseDetails = ''; + if (responseType != null && response != null) { + responseDetails = + '-----RESPONSE_DETAILS-----\nRESPONSE_TYPE: $responseType\nRESPONSE_BODY: $response'; + } + + return """REST API (HTTP) +METHOD: $method +ENDPOINT: $endpoint +HEADERS: ${headersStr.isEmpty ? '{}' : headersStr} +$queryParamStr +$bodyDetails + +"""; + } +} diff --git a/lib/apitoolgen/tool_templates.dart b/lib/apitoolgen/tool_templates.dart new file mode 100644 index 00000000..c36db4f2 --- /dev/null +++ b/lib/apitoolgen/tool_templates.dart @@ -0,0 +1,93 @@ +const GENERAL_ARG_PROPERTY_FORMAT_PY = """:ARG_NAME: { + "type": ":ARG_TYPE:", + "description: ":ARG_DESC:" +}"""; + +const GENERAL_PYTHON_TOOL_FORMAT = """ +:FUNC: + +api_tool = { + "function": func, + "definition": { + "name": ":TOOL_NAME:", + "description": ":TOOL_DESCRIPTION:", + "parameters": { + "type": "object", + "properties": :TOOL_PARAMS:, + "required": [:REQUIRED_PARAM_NAMES:], + "additionalProperties": False + } + } +} + +__all__ = ["api_tool"] +"""; + +const GENERAL_JAVASCRIPT_TOOL_FORMAT = """ +:FUNC: + +const apiTool = { + function: func, + definition: { + type: 'function', + function: { + name: ':TOOL_NAME:', + description: ':TOOL_DESCRIPTION:', + parameters: { + type: 'object', + properties: :TOOL_PARAMS:, + required: [:REQUIRED_PARAM_NAMES:] + additionalProperties: false + } + } + } +}; + +export { apiTool }; +"""; + +const LANGCHAIN_PYTHON_TOOL_FORMAT = """ +from langchain.tools import StructuredTool + +:INPUT_SCHEMA: + +:FUNC: + +api_tool = StructuredTool.from_function( + func=func, + name=":TOOL_NAME:", + description=":TOOL_DESCRIPTION:", + args_schema=INPUT_SCHEMA, +) +__all__ = ["api_tool"] +"""; + +const LANGCHAIN_JAVASCRIPT_TOOL_FORMAT = """ +import { DynamicStructuredTool } from 'langchain/tools'; +import { z } from 'zod'; + +:INPUT_SCHEMA: + +:FUNC: + +const apiTool = new DynamicStructuredTool({ + func: func, + name: ':TOOL_NAME:', + description: ':TOOL_DESCRIPTION:', + schema: INPUT_SCHEMA +}); + +export { apiTool }; +"""; + +const MICROSOFT_AUTOGEN_TOOL_FORMAT = """ +:FUNC: + +api_tool = { + "function": func, + "name": ":TOOL_NAME:", + "description": ":TOOL_DESCRIPTION:" +} + +__all__ = ["api_tool"] +"""; diff --git a/lib/services/agentic_services/agents/agents.dart b/lib/services/agentic_services/agents/agents.dart index e7ae4755..ae01f493 100644 --- a/lib/services/agentic_services/agents/agents.dart +++ b/lib/services/agentic_services/agents/agents.dart @@ -3,3 +3,4 @@ export 'semantic_analyser.dart'; export 'stac2flutter.dart'; export 'stacgen.dart'; export 'stacmodifier.dart'; +export 'apitool_funcgen.dart'; diff --git a/lib/services/agentic_services/agents/apitool_funcgen.dart b/lib/services/agentic_services/agents/apitool_funcgen.dart new file mode 100644 index 00000000..022b09e6 --- /dev/null +++ b/lib/services/agentic_services/agents/apitool_funcgen.dart @@ -0,0 +1,47 @@ +import 'package:genai/agentic_engine/blueprint.dart'; + +const String _sysprompt = """ +You are an expert LANGUAGE SPECIFIC API CALL METHOD Creator. +You will be provided a complete API Details Text named (REQDATA) which consists of the method, endpoint, params, headers, body +and so on. +You are also provided with a Target Language named (TARGET_LANGUAGE). + +Using this data, Create a method EXPLICITLY named `func`. +The method `func` should accept any variables if present (refer REQDATA for all details) and include it as part of the arguments + +Use the Industry standard best practices while calling the API provided by REQDATA. +If REQDATA contains any Authorization (Eg: bearer key), embed that into the function itself and dont expect it to be passed in the function +same goes for any header details. + +REQDATA: :REQDATA: + +TARGET_LANGUAGE: :TARGET_LANGUAGE: + +if REQDATA.BODY_TYPE is TEXT => use it as-is +if REQDATA.BODY_TYPE is JSON or FORM-DATA => use the types provided to create variables passed from the function arguments + +ALWAYS return the output as code only and do not start or begin with any introduction or conclusion. ONLY THE CODE. +"""; + +class APIToolFunctionGenerator extends APIDashAIAgent { + @override + String get agentName => 'APITOOL_FUNCGEN'; + + @override + String getSystemPrompt() { + return _sysprompt; + } + + @override + Future validator(String aiResponse) async { + //Add any specific validations here as needed + return true; + } + + @override + Future outputFormatter(String validatedResponse) async { + return { + 'FUNC': validatedResponse, + }; + } +} diff --git a/lib/widgets/response_pane_header.dart b/lib/widgets/response_pane_header.dart index 6e7fa74a..13f0d135 100644 --- a/lib/widgets/response_pane_header.dart +++ b/lib/widgets/response_pane_header.dart @@ -1,9 +1,16 @@ +import 'dart:convert'; + +import 'package:apidash/apitoolgen/request_consolidator.dart'; import 'package:apidash/providers/collection_providers.dart'; +import 'package:apidash/services/agentic_services/agent_caller.dart'; +import 'package:apidash/services/agentic_services/agents/apitool_funcgen.dart'; import 'package:apidash/widgets/ai_ui_desginer_widgets.dart'; +import 'package:apidash_core/apidash_core.dart'; import 'package:flutter/material.dart'; import 'package:apidash_design_system/apidash_design_system.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; +import 'package:genai/agentic_engine/blueprint.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'button_clear_response.dart'; @@ -27,62 +34,142 @@ class ResponsePaneHeader extends ConsumerWidget { return Padding( padding: kPv8, child: SizedBox( - height: kHeaderHeight, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + // height: kHeaderHeight, + child: Column( children: [ - kHSpacer10, - Expanded( - child: Text( - "$responseStatus: ${message ?? '-'}", - softWrap: false, - overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontFamily: kCodeStyle.fontFamily, - color: getResponseStatusCodeColor( - responseStatus, - brightness: Theme.of(context).brightness, - ), - ), - ), - ), - FilledButton.tonalIcon( - style: FilledButton.styleFrom( - padding: kPh12, - minimumSize: const Size(44, 44), - ), - onPressed: () { - final model = ref.watch(selectedRequestModelProvider - .select((value) => value?.httpResponseModel)); - if (model == null) return; - final body = (model.sseOutput?.isNotEmpty ?? false) - ? model.sseOutput?.join("\n") - : model.formattedBody ?? model.body; - showCustomDialog(context, body ?? ""); - }, - icon: Icon( - Icons.generating_tokens, - ), - label: const SizedBox( - child: Text( - kLabelGenerateUI, - ), - ), - ), - kHSpacer10, - Text( - humanizeDuration(time), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontFamily: kCodeStyle.fontFamily, - color: Theme.of(context).colorScheme.secondary, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + kHSpacer10, + Expanded( + child: Text( + "$responseStatus: ${message ?? '-'}", + softWrap: false, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontFamily: kCodeStyle.fontFamily, + color: getResponseStatusCodeColor( + responseStatus, + brightness: Theme.of(context).brightness, + ), + ), ), + ), + kHSpacer10, + Text( + humanizeDuration(time), + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontFamily: kCodeStyle.fontFamily, + color: Theme.of(context).colorScheme.secondary, + ), + ), + kHSpacer10, + showClearButton + ? ClearResponseButton( + onPressed: onClearResponse, + ) + : const SizedBox.shrink(), + ], ), - kHSpacer10, - showClearButton - ? ClearResponseButton( - onPressed: onClearResponse, - ) - : const SizedBox.shrink(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () async { + final requestModel = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpRequestModel)); + final responseModel = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + + if (requestModel == null) return; + if (responseModel == null) { + print("AA"); + return; + } + String? bodyTXT; + Map? bodyJSON; + List? bodyFormData; + + if (requestModel.bodyContentType == ContentType.formdata) { + bodyFormData = requestModel.formDataMapList; + } else if (requestModel.bodyContentType == + ContentType.json) { + bodyJSON = jsonDecode(requestModel.body.toString()); + } else { + bodyTXT = requestModel.body!; + } + + final reqDesModel = APIDashRequestDescription( + endpoint: requestModel.url, + method: requestModel.method.name.toUpperCase(), + responseType: responseModel.contentType.toString(), + headers: requestModel.headersMap, + response: responseModel.body, + formData: bodyFormData, + bodyTXT: bodyTXT, + bodyJSON: bodyJSON, + ); + + print(reqDesModel.generateREQDATA); + return; + + final x = await APIDashAgentCaller.instance.call( + APIToolFunctionGenerator(), + ref: ref, + input: AgentInputs(variables: { + 'REQDATA': reqDesModel.generateREQDATA, + 'TARGET_LANGUAGE': 'JAVASCRIPT' + }), + ); + + print(x); + + // print(reqDesModel.generateREQDATA); + + // final model = ref.watch(selectedRequestModelProvider + // .select((value) => value?.httpResponseModel)); + // showCustomDialog(context, model?.formattedBody ?? ""); + }, + icon: Icon( + Icons.token_outlined, + ), + label: const SizedBox( + child: Text( + "Generate Tool", + ), + ), + ), + kHSpacer10, + FilledButton.tonalIcon( + style: FilledButton.styleFrom( + padding: kPh12, + minimumSize: const Size(44, 44), + ), + onPressed: () { + final model = ref.watch(selectedRequestModelProvider + .select((value) => value?.httpResponseModel)); + if (model == null) return; + final body = (model.sseOutput?.isNotEmpty ?? false) + ? model.sseOutput?.join("\n") + : model.formattedBody ?? model.body; + showCustomDialog(context, body ?? ""); + }, + icon: Icon( + Icons.generating_tokens, + ), + label: const SizedBox( + child: Text( + kLabelGenerateUI, + ), + ), + ), + kHSpacer10, + ], + ) ], ), ),