Skip to content

Commit 9aeedfb

Browse files
authored
Merge pull request #979 from ShashwatXD/populate-params-on-import
fix: populated params if api has params
2 parents 2b07733 + c0ffc06 commit 9aeedfb

File tree

20 files changed

+1987
-690
lines changed

20 files changed

+1987
-690
lines changed
Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
11
String buildCurlInsightsPrompt({
2-
required String curlSummary,
3-
Map<String, dynamic>? diff,
4-
Map<String, dynamic>? current,
2+
String? diff,
3+
Map<String, dynamic>? newReq,
54
}) {
65
return """
7-
<system_prompt>
8-
YOU ARE Dashbot, an API Insights Assistant specialized in analyzing cURL commands within API Dash.
6+
YOU ARE API Insights Assistant specialized in analyzing API Requests.
97
108
STRICT OFF-TOPIC POLICY
119
- If a request is unrelated to APIs, refuse. Return JSON with only "explanation" and an empty "actions": [].
1210
13-
CONTEXT (CURL SUMMARY)
14-
${curlSummary.trim()}
11+
REFUSAL TEMPLATE (when off-topic), JSON only:
12+
{"explanation":"I am Dashbot, an AI assistant focused specifically on API development tasks within API Dash. My capabilities are limited to explaining API responses, debugging requests, generating documentation, creating tests, visualizing API data, and generating integration code. Therefore, I cannot answer questions outside of this scope. How can I assist you with an API-related task?","actions":[]}
1513
16-
CONTEXT (DIFF VS CURRENT REQUEST, JSON)
17-
${diff ?? '{}'}
14+
Parsed request:
15+
$newReq
1816
19-
CONTEXT (CURRENT REQUEST SNAPSHOT, JSON)
20-
${current ?? '{}'}
17+
Changes from existing Request:
18+
$diff
2119
2220
TASK
23-
- Provide practical, user-friendly insights based on the cURL:
21+
- Provide practical, user-friendly insights based on the Parsed request & Changes from existing Request:
2422
- Start with a short 1–2 line paragraph summary.
2523
- Then provide 5–8 concise bullet points with key insights (method/url change, headers added/updated, params, body type/size, auth/security notes).
2624
- Provide a short preview of changes if applied (bulleted), and any caveats (overwriting headers/body, missing tokens).
@@ -31,10 +29,7 @@ OUTPUT FORMAT (STRICT)
3129
- Return ONLY a single JSON object.
3230
- Keys: {"explanation": string, "actions": []}
3331
34-
REFUSAL TEMPLATE (when off-topic), JSON only:
35-
{"explanation":"I am Dashbot, an AI assistant focused specifically on API development tasks within API Dash. My capabilities are limited to explaining API responses, debugging requests, generating documentation, creating tests, visualizing API data, and generating integration code. Therefore, I cannot answer questions outside of this scope. How can I assist you with an API-related task?","actions":[]}
36-
37-
RETURN THE JSON ONLY.
38-
</system_prompt>
32+
OUTPUT
33+
{"explanation":
3934
""";
4035
}

lib/dashbot/prompts/dashbot_prompts.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,14 +170,12 @@ class DashbotPrompts {
170170

171171
// Provide insights after parsing a cURL command
172172
String curlInsightsPrompt({
173-
required String curlSummary,
174-
Map<String, dynamic>? diff,
175-
Map<String, dynamic>? current,
173+
String? diff,
174+
Map<String, dynamic>? newReq,
176175
}) {
177176
return buildCurlInsightsPrompt(
178-
curlSummary: curlSummary,
179177
diff: diff,
180-
current: current,
178+
newReq: newReq,
181179
);
182180
}
183181
}

lib/dashbot/providers/chat_viewmodel.dart

Lines changed: 59 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,20 @@ import '../utils/utils.dart';
1414
import 'dashbot_active_route_provider.dart';
1515
import 'service_providers.dart';
1616

17+
final chatViewmodelProvider =
18+
StateNotifierProvider<ChatViewmodel, ChatState>((ref) {
19+
return ChatViewmodel(ref);
20+
});
21+
1722
class ChatViewmodel extends StateNotifier<ChatState> {
1823
ChatViewmodel(this._ref) : super(const ChatState());
1924

2025
final Ref _ref;
2126

2227
ChatRemoteRepository get _repo => _ref.read(chatRepositoryProvider);
2328
RequestModel? get _currentRequest => _ref.read(selectedRequestModelProvider);
29+
HttpRequestModel? get _currentSubstitutedHttpRequestModel =>
30+
_ref.read(selectedSubstitutedHttpRequestModelProvider);
2431
AIRequestModel? get _selectedAIModel {
2532
final json = _ref.read(settingsProvider).defaultAIModel;
2633
if (json == null) return null;
@@ -120,9 +127,7 @@ class ChatViewmodel extends StateNotifier<ChatState> {
120127
final currentReq = _currentRequest;
121128
final substitutedReq = (currentReq?.httpRequestModel != null)
122129
? currentReq!.copyWith(
123-
httpRequestModel:
124-
_getSubstitutedHttpRequestModel(currentReq.httpRequestModel!),
125-
)
130+
httpRequestModel: _currentSubstitutedHttpRequestModel?.copyWith())
126131
: currentReq;
127132
String systemPrompt;
128133
if (type == ChatMessageType.generateCode) {
@@ -469,45 +474,36 @@ class ChatViewmodel extends StateNotifier<ChatState> {
469474
state = state.copyWith(isGenerating: true, currentStreamingResponse: '');
470475
try {
471476
debugPrint('[cURL] Original: $trimmed');
472-
final curl = CurlImportService.tryParseCurl(trimmed);
477+
final curl = Curl.tryParse(trimmed);
473478
if (curl == null) {
474479
_appendSystem(
475480
'I couldn\'t parse that cURL command. Please check that it:\n- Starts with `curl `\n- Has balanced quotes (wrap JSON bodies in single quotes)\n- Uses backslashes for multi-line commands (if any)\n\nFix the command and paste it again below.\n\nExample:\n\ncurl -X POST https://api.apidash.dev/users \\\n -H \'Content-Type: application/json\'',
476481
ChatMessageType.importCurl,
477482
);
478483
return;
479484
}
480-
final currentCtx = _currentRequestContext();
485+
final currentSubstitutedHttpRequestJson =
486+
_currentSubstitutedHttpRequestModel?.toJson();
487+
final payload = convertCurlToHttpRequestModel(curl).toJson();
481488
// Prepare base message first (without AI insights)
482489
var built = CurlImportService.buildResponseFromParsed(
483-
curl,
484-
current: currentCtx,
490+
payload,
491+
currentJson: currentSubstitutedHttpRequestJson,
485492
);
486493
var msg = jsonDecode(built.jsonMessage) as Map<String, dynamic>;
487494

488495
// Ask AI for cURL insights
489496
try {
490497
final ai = _selectedAIModel;
491498
if (ai != null) {
492-
final summary = CurlImportService.summaryForPayload(
493-
jsonDecode(built.jsonMessage)['actions'][0]['value']
494-
as Map<String, dynamic>,
495-
);
496-
final diff = CurlImportService.diffForPayload(
497-
jsonDecode(built.jsonMessage)['actions'][0]['value']
498-
as Map<String, dynamic>,
499-
currentCtx,
500-
);
499+
final diff = jsonDecode(built.jsonMessage)['meta']['diff'] as String;
501500
final sys = dash.DashbotPrompts().curlInsightsPrompt(
502-
curlSummary: summary,
503501
diff: diff,
504-
current: currentCtx,
502+
newReq: payload,
505503
);
506504
final res = await _repo.sendChat(
507505
request: ai.copyWith(
508506
systemPrompt: sys,
509-
userPrompt:
510-
'Provide concise, actionable insights about this cURL import.',
511507
stream: false,
512508
),
513509
);
@@ -530,7 +526,7 @@ class ChatViewmodel extends StateNotifier<ChatState> {
530526
: <String, dynamic>{};
531527
final enriched = CurlImportService.buildActionMessageFromPayload(
532528
payload,
533-
current: currentCtx,
529+
current: currentSubstitutedHttpRequestJson,
534530
insights: insights,
535531
);
536532
msg = enriched;
@@ -575,39 +571,9 @@ class ChatViewmodel extends StateNotifier<ChatState> {
575571
}
576572

577573
Map<String, dynamic>? _currentRequestContext() {
578-
final originalRq = _currentRequest?.httpRequestModel;
574+
final originalRq = _currentSubstitutedHttpRequestModel;
579575
if (originalRq == null) return null;
580-
final rq = _getSubstitutedHttpRequestModel(originalRq);
581-
final headers = <String, String>{};
582-
for (final h in rq.headers ?? const []) {
583-
final k = (h.name).toString();
584-
final v = (h.value ?? '').toString();
585-
if (k.isNotEmpty) headers[k] = v;
586-
}
587-
final params = <String, String>{};
588-
for (final p in rq.params ?? const []) {
589-
final k = (p.name).toString();
590-
final v = (p.value ?? '').toString();
591-
if (k.isNotEmpty) params[k] = v;
592-
}
593-
final body = rq.body ?? '';
594-
final formData = (rq.formData ?? const [])
595-
.map((f) => {
596-
'name': f.name,
597-
'value': f.value,
598-
'type': f.type.name,
599-
})
600-
.toList();
601-
final isForm = rq.bodyContentType == ContentType.formdata;
602-
return {
603-
'method': rq.method.name.toUpperCase(),
604-
'url': rq.url,
605-
'headers': headers,
606-
'params': params,
607-
'body': body,
608-
'form': isForm,
609-
'formData': formData,
610-
};
576+
return originalRq.toJson();
611577
}
612578

613579
Future<void> handleOpenApiAttachment(ChatAttachment att) async {
@@ -827,99 +793,47 @@ class ChatViewmodel extends StateNotifier<ChatState> {
827793
}
828794

829795
Future<void> _applyCurl(ChatAction action) async {
830-
final requestId = _currentRequest?.id;
831-
final collection = _ref.read(collectionStateNotifierProvider.notifier);
832-
final payload = action.value is Map<String, dynamic>
833-
? (action.value as Map<String, dynamic>)
834-
: <String, dynamic>{};
835-
836-
String methodStr = (payload['method'] as String?)?.toLowerCase() ?? 'get';
837-
final method = HTTPVerb.values.firstWhere(
838-
(m) => m.name == methodStr,
839-
orElse: () => HTTPVerb.get,
840-
);
841-
final url = payload['url'] as String? ?? '';
842-
final baseUrl = _inferBaseUrl(url);
843-
844-
final headersMap =
845-
(payload['headers'] as Map?)?.cast<String, dynamic>() ?? {};
846-
final headers = headersMap.entries
847-
.map((e) => NameValueModel(name: e.key, value: e.value.toString()))
848-
.toList();
849-
850-
final body = payload['body'] as String?;
851-
final formFlag = payload['form'] == true;
852-
final formDataListRaw = (payload['formData'] as List?)?.cast<dynamic>();
853-
final formData = formDataListRaw == null
854-
? <FormDataModel>[]
855-
: formDataListRaw
856-
.whereType<Map>()
857-
.map((e) => FormDataModel(
858-
name: (e['name'] as String?) ?? '',
859-
value: (e['value'] as String?) ?? '',
860-
type: (() {
861-
final t = (e['type'] as String?) ?? 'text';
862-
try {
863-
return FormDataType.values
864-
.firstWhere((ft) => ft.name == t);
865-
} catch (_) {
866-
return FormDataType.text;
867-
}
868-
})(),
869-
))
870-
.toList();
871-
872-
ContentType bodyContentType;
873-
if (formFlag || formData.isNotEmpty) {
874-
bodyContentType = ContentType.formdata;
875-
} else if ((body ?? '').trim().isEmpty) {
876-
bodyContentType = ContentType.text;
877-
} else {
878-
// Heuristic JSON detection
879-
try {
880-
jsonDecode(body!);
881-
bodyContentType = ContentType.json;
882-
} catch (_) {
883-
bodyContentType = ContentType.text;
796+
try {
797+
final requestId = _currentRequest?.id;
798+
final payload = action.value is Map<String, dynamic>
799+
? (action.value as Map<String, dynamic>)
800+
: <String, dynamic>{};
801+
final httpRequestModel = HttpRequestModel.fromJson(payload);
802+
final baseUrl = _inferBaseUrl(httpRequestModel.url);
803+
final withEnvUrl =
804+
await _maybeSubstituteBaseUrl(httpRequestModel.url, baseUrl);
805+
806+
if (action.field == 'apply_to_selected') {
807+
if (requestId == null) return;
808+
_ref.read(collectionStateNotifierProvider.notifier).update(
809+
method: httpRequestModel.method,
810+
url: withEnvUrl,
811+
headers: httpRequestModel.headers,
812+
isHeaderEnabledList: List<bool>.filled(
813+
httpRequestModel.headers?.length ?? 0, true),
814+
body: httpRequestModel.body,
815+
bodyContentType: httpRequestModel.bodyContentType,
816+
formData: httpRequestModel.formData,
817+
params: httpRequestModel.params,
818+
isParamEnabledList:
819+
List<bool>.filled(httpRequestModel.params?.length ?? 0, true),
820+
authModel: null,
821+
);
822+
_appendSystem('Applied cURL to the selected request.',
823+
ChatMessageType.importCurl);
824+
} else if (action.field == 'apply_to_new') {
825+
final model = httpRequestModel.copyWith(
826+
url: withEnvUrl,
827+
);
828+
_ref
829+
.read(collectionStateNotifierProvider.notifier)
830+
.addRequestModel(model, name: 'Imported cURL');
831+
_appendSystem(
832+
'Created a new request from the cURL.', ChatMessageType.importCurl);
884833
}
885-
}
886-
887-
final withEnvUrl = await _maybeSubstituteBaseUrl(url, baseUrl);
888-
if (action.field == 'apply_to_selected') {
889-
if (requestId == null) return;
890-
// Replacement semantics: ensure previous body/formData are cleared if absent in cURL
891-
final replacingBody =
892-
(formFlag || formData.isNotEmpty) ? '' : (body ?? '');
893-
final replacingFormData =
894-
formData.isEmpty ? const <FormDataModel>[] : formData;
895-
collection.update(
896-
method: method,
897-
url: withEnvUrl,
898-
headers: headers,
899-
isHeaderEnabledList: List<bool>.filled(headers.length, true),
900-
body: replacingBody,
901-
bodyContentType: bodyContentType,
902-
formData: replacingFormData,
903-
// Wipe existing parameters and authentication to ensure clean state
904-
params: const [],
905-
isParamEnabledList: const [],
906-
authModel: null,
907-
);
908-
_appendSystem(
909-
'Applied cURL to the selected request.', ChatMessageType.importCurl);
910-
} else if (action.field == 'apply_to_new') {
911-
final model = HttpRequestModel(
912-
method: method,
913-
url: withEnvUrl,
914-
headers: headers,
915-
isHeaderEnabledList: List<bool>.filled(headers.length, true),
916-
body: body,
917-
bodyContentType: bodyContentType,
918-
formData: formData.isEmpty ? null : formData,
919-
);
920-
collection.addRequestModel(model, name: 'Imported cURL');
921-
_appendSystem(
922-
'Created a new request from the cURL.', ChatMessageType.importCurl);
834+
} catch (e) {
835+
_appendSystem('Error encountered while importing cURL - $e',
836+
ChatMessageType.importCurl);
923837
}
924838
}
925839

@@ -1004,21 +918,4 @@ class ChatViewmodel extends StateNotifier<ChatState> {
1004918
),
1005919
);
1006920
}
1007-
1008-
HttpRequestModel _getSubstitutedHttpRequestModel(
1009-
HttpRequestModel httpRequestModel) {
1010-
final envMap = _ref.read(availableEnvironmentVariablesStateProvider);
1011-
final activeEnvId = _ref.read(activeEnvironmentIdStateProvider);
1012-
return substituteHttpRequestModel(
1013-
httpRequestModel,
1014-
envMap,
1015-
activeEnvId,
1016-
);
1017-
}
1018921
}
1019-
1020-
final chatViewmodelProvider = StateNotifierProvider<ChatViewmodel, ChatState>((
1021-
ref,
1022-
) {
1023-
return ChatViewmodel(ref);
1024-
});

0 commit comments

Comments
 (0)