Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions workspaces/ballerina/ballerina-core/src/interfaces/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,25 @@ export interface StatusCodeResponse extends PropertyModel {
mediaType: PropertyModel;
}

export interface PayloadContext {
export interface HttpPayloadContext {
protocol: "HTTP";
serviceName: string;
serviceBasePath: string;
resourceBasePath?: string;
resourceMethod?: string;
resourceDocumentation?:string;
paramDetails?:ParamDetails[];
resourceDocumentation?: string;
paramDetails?: ParamDetails[];
}

export interface MessageQueuePayloadContext {
protocol: "MESSAGE_BROKER";
serviceName: string;
queueOrTopic?: string;
messageDocumentation?: string;
}

export type PayloadContext = HttpPayloadContext | MessageQueuePayloadContext;

export interface ParamDetails {
name: string;
type: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { getPayloadGenerationSystemPrompt, getPayloadGenerationUserPrompt } from
* @throws Error if generation fails or response cannot be parsed
*/
export async function generateExamplePayload(context: PayloadContext): Promise<object> {
const systemPrompt = getPayloadGenerationSystemPrompt();
const systemPrompt = getPayloadGenerationSystemPrompt(context);
const userPrompt = getPayloadGenerationUserPrompt(context);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
// specific language governing permissions and limitations
// under the License.

import { PayloadContext } from "@wso2/ballerina-core";
import { PayloadContext, HttpPayloadContext, MessageQueuePayloadContext } from "@wso2/ballerina-core";
import { isMessageQueuePayloadContext } from "../../utils";

/**
* System prompt for generating example JSON payloads
*/
export function getPayloadGenerationSystemPrompt(): string {
return `You are an expert at generating realistic and contextually appropriate example JSON payloads for REST API services.
export function getSystemPrompt(serviceType: string): string {

return `You are an expert at generating realistic and contextually appropriate example JSON payloads for ${serviceType} services.

Your task is to analyze the provided service context and generate a meaningful JSON payload that:
1. Matches the semantic intent of the service and resource
Expand All @@ -43,9 +45,9 @@ Return ONLY the JSON payload object in the specified format.`;
}

/**
* User prompt for generating example JSON payloads
* User prompt for generating example JSON payloads for HTTP services
*/
export function getPayloadGenerationUserPrompt(context: PayloadContext): string {
function getHttpUserPrompt(context: HttpPayloadContext): string {
let prompt = `Generate an example JSON payload for the following HTTP service resource:\n\n`;

prompt += `**Service Name:** ${context.serviceName}\n`;
Expand Down Expand Up @@ -78,3 +80,51 @@ export function getPayloadGenerationUserPrompt(context: PayloadContext): string

return prompt;
}

/**
* User prompt for generating example JSON payloads for message brokers
*/
function getMessageBrokerUserPrompt(context: MessageQueuePayloadContext): string {
let prompt = `Generate an example JSON payload for the following message broker service:\n\n`;

prompt += `**Service Name:** ${context.serviceName}\n`;

if (context.queueOrTopic) {
prompt += `**Queue/Topic Name:** ${context.queueOrTopic}\n`;
}

if (context.messageDocumentation) {
prompt += `**Message Documentation:** ${context.messageDocumentation}\n`;
}

prompt += `\n**IMPORTANT:** Generate ONLY the message payload value that would be consumed by the application logic.
DO NOT include message broker metadata fields such as topic, partition, offset, timestamp, key, headers, exchange, routing_key,
delivery_tag, or any other transport-level metadata.
Generate ONLY the business data object that represents the actual message content.

Based on the above context, generate a realistic and meaningful example JSON payload that represents the business data for this message.`;

return prompt;
}

/**
* System prompt for generating example JSON payloads
* Automatically selects the appropriate prompt based on the payload context type
*/
export function getPayloadGenerationSystemPrompt(context?: PayloadContext): string {
if (context && isMessageQueuePayloadContext(context)) {
return getSystemPrompt("Message Broker");
}
return getSystemPrompt("REST API");
}

/**
* User prompt for generating example JSON payloads
* Automatically selects the appropriate prompt based on the payload context type
*/
export function getPayloadGenerationUserPrompt(context: PayloadContext): string {
if (isMessageQueuePayloadContext(context)) {
return getMessageBrokerUserPrompt(context);
}
return getHttpUserPrompt(context as HttpPayloadContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ import {
ToolCall,
ToolResult,
Command,
DocumentationGeneratorIntermediaryState
DocumentationGeneratorIntermediaryState,
PayloadContext,
HttpPayloadContext,
MessageQueuePayloadContext
} from "@wso2/ballerina-core";
import { ModelMessage } from "ai";
import { MessageRole } from "./types";
Expand Down Expand Up @@ -259,3 +262,11 @@ export function getErrorMessage(error: unknown): string {
return String(error);
}
}

export function isHttpPayloadContext(context: PayloadContext): context is HttpPayloadContext {
return context.protocol === "HTTP";
}

export function isMessageQueuePayloadContext(context: PayloadContext): context is MessageQueuePayloadContext {
return context.protocol === "MESSAGE_BROKER";
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*/

import styled from "@emotion/styled";
import { FunctionModel, ParameterModel, Type } from "@wso2/ballerina-core";
import { ConfigProperties, FunctionModel, ParameterModel, MessageQueuePayloadContext, Type } from "@wso2/ballerina-core";
import {
ActionButtons,
CheckBox,
Expand Down Expand Up @@ -99,12 +99,16 @@ export const EditorContentColumn = styled.div`
export interface DatabindFormProps {
model: FunctionModel;
isSaving?: boolean;
onSave: (functionModel: FunctionModel) => void;
onSave: (functionModel: FunctionModel, openDiagram?: boolean) => void;
onClose: () => void;
isNew?: boolean;
payloadContext?: MessageQueuePayloadContext;
serviceProperties?: ConfigProperties;
serviceModuleName?: string;
}

export function DatabindForm(props: DatabindFormProps) {
const { model, isSaving = false, onSave, onClose } = props;
const { model, isSaving = false, onSave, onClose, isNew = false, payloadContext, serviceProperties, serviceModuleName } = props;

const [isLoading, setIsLoading] = useState<boolean>(false);
const [functionModel, setFunctionModel] = useState<FunctionModel>(model);
Expand All @@ -113,7 +117,6 @@ export function DatabindForm(props: DatabindFormProps) {

// State for payload editor
const [editModel, setEditModel] = useState<ParameterModel | undefined>(undefined);
const [isNew, setIsNew] = useState<boolean>(false);
const [editingIndex, setEditingIndex] = useState<number>(-1);

// State for type editor modal
Expand Down Expand Up @@ -141,6 +144,31 @@ export function DatabindForm(props: DatabindFormProps) {
return parameterDescriptionMap[key] || "";
};

/**
* Gets the queue name description string based on module name
* @param moduleName - The module name (e.g., "rabbitmq", "kafka")
* @returns Description string about where the payload comes from
*/
const getQueueDescriptionByModule = (moduleName: string): string => {
if (!moduleName) {
return "";
}
const lowerModuleName = moduleName.toLowerCase();
if (lowerModuleName === "rabbitmq") {
return serviceProperties.stringLiteral?.value;
} else if (lowerModuleName === "kafka") {
const metaValue = serviceProperties?.readOnlyMetaData?.value;
if (metaValue && typeof metaValue === "object") {
for (const [key, val] of Object.entries(metaValue as Record<string, any>)) {
if (key === "Topics" && Array.isArray(val) && val.length > 0) {
return String(val[0]);
}
}
}
}
return "";
};

const handleParamChange = (params: ParameterModel[]) => {
const updatedFunctionModel = {
...functionModel,
Expand Down Expand Up @@ -175,7 +203,8 @@ export function DatabindForm(props: DatabindFormProps) {
};

const handleSave = () => {
onSave(functionModel);
// For new handlers, always open diagram after save
onSave(functionModel, isNew);
};

const handleCancel = () => {
Expand Down Expand Up @@ -255,14 +284,13 @@ export function DatabindForm(props: DatabindFormProps) {
p => p.metadata.label === param.metadata.label && p.name.value === param.name.value
);
setEditingIndex(index);
setIsNew(false);
setEditModel(param);
};

const onChangeParam = (param: ParameterModel) => {
setEditModel(param);
// Update the parameters array in real-time for existing parameters
if (!isNew && editingIndex >= 0) {
if (editingIndex >= 0) {
const updatedParameters = [...functionModel.parameters];
updatedParameters[editingIndex] = param;
handleParamChange(updatedParameters);
Expand All @@ -280,23 +308,18 @@ export function DatabindForm(props: DatabindFormProps) {
}
}

if (isNew) {
handleParamChange([...functionModel.parameters, param]);
setIsNew(false);
// Use the editingIndex for more reliable updates
if (editingIndex >= 0) {
const updatedParameters = [...functionModel.parameters];
updatedParameters[editingIndex] = param;
handleParamChange(updatedParameters);
} else {
// Use the editingIndex for more reliable updates
if (editingIndex >= 0) {
const updatedParameters = [...functionModel.parameters];
updatedParameters[editingIndex] = param;
handleParamChange(updatedParameters);
} else {
// Fallback to the original logic if index is not available
handleParamChange(
functionModel.parameters.map((p) =>
p.metadata.label === param.metadata.label && p.name.value === param.name.value ? param : p
)
);
}
// Fallback to the original logic if index is not available
handleParamChange(
functionModel.parameters.map((p) =>
p.metadata.label === param.metadata.label && p.name.value === param.name.value ? param : p
)
);
}
setEditModel(undefined);
setEditingIndex(-1);
Expand Down Expand Up @@ -541,6 +564,10 @@ export function DatabindForm(props: DatabindFormProps) {
initialTypeName={generatePayloadTypeName()}
editMode={false}
modalTitle={"Define " + payloadFieldName + " Schema"}
payloadContext={{
...payloadContext,
queueOrTopic: getQueueDescriptionByModule(serviceModuleName)
}}
modalWidth={650}
modalHeight={600}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { Codicon, Divider, LinkButton, Typography, CheckBox, CheckBoxGroup, Them
import styled from '@emotion/styled';
import { ParamEditor } from './ParamEditor';
import { ParamItem } from './ParamItem';
import { ConfigProperties, ParameterModel, PayloadContext, PropertyModel, Type } from '@wso2/ballerina-core';
import { ConfigProperties, ParameterModel, HttpPayloadContext, PropertyModel, Type } from '@wso2/ballerina-core';
import { ContextBasedFormTypeEditor } from '../../../../../../components/ContextBasedFormTypeEditor';

export interface ParametersProps {
Expand All @@ -34,7 +34,7 @@ export interface ParametersProps {
pathName?: string;
showPayload: boolean;
isNewResource?: boolean;
payloadContext?: PayloadContext;
payloadContext?: HttpPayloadContext;
}

const AddButtonWrapper = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ResourcePath, verbs } from './ResourcePath/ResourcePath';
import { ResourceResponse } from './ResourceResponse/ResourceResponse';
import styled from '@emotion/styled';
import { getDefaultResponse, HTTP_METHOD, removeForwardSlashes, sanitizedHttpPath } from '../../utils';
import { ConfigProperties, FunctionModel, ParameterModel, PayloadContext, PropertyModel, ReturnTypeModel } from '@wso2/ballerina-core';
import { ConfigProperties, FunctionModel, ParameterModel, HttpPayloadContext, PropertyModel, ReturnTypeModel } from '@wso2/ballerina-core';
import { Parameters } from './Parameters/Parameters';
import { PanelContainer } from '@wso2/ballerina-side-panel';
import { ResourceConfig } from './ResourceConfig/ResourceConfig';
Expand Down Expand Up @@ -88,7 +88,7 @@ export interface ResourceFormProps {
onSave: (functionModel: FunctionModel, openDiagram?: boolean) => void;
onClose: () => void;
isNew?: boolean;
payloadContext?: PayloadContext;
payloadContext?: HttpPayloadContext;
filePath?: string;
}

Expand Down
Loading
Loading