diff --git a/example-servers/sveltekit/package-lock.json b/example-servers/sveltekit/package-lock.json
index ba3c676fe..71b0d6e5f 100644
--- a/example-servers/sveltekit/package-lock.json
+++ b/example-servers/sveltekit/package-lock.json
@@ -9,7 +9,8 @@
"version": "0.0.1",
"dependencies": {
"deep-chat": "^2.2.2",
- "eventsource-parser": "^1.1.2"
+ "eventsource-parser": "^1.1.2",
+ "salesforce-agent-api-client": "^1.0.1"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.1.1",
@@ -1261,6 +1262,27 @@
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"dev": true
},
+ "node_modules/eventsource-client": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/eventsource-client/-/eventsource-client-1.1.3.tgz",
+ "integrity": "sha512-6GJGePDMxin/6VH1Dex7RqnZRguweO1BVKhXpBYwKcQbVLOZPLfeDr9vYSb9ra88kjs0NpT8FaEDz5rExedDkg==",
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-client/node_modules/eventsource-parser": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz",
+ "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/eventsource-parser": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz",
@@ -2119,6 +2141,15 @@
}
]
},
+ "node_modules/salesforce-agent-api-client": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/salesforce-agent-api-client/-/salesforce-agent-api-client-1.0.1.tgz",
+ "integrity": "sha512-Q6zoq2mNH/Ai++rixXtQmecTuN0pAgkK3xZc55xla8QnUAqZ1F8o+d2aRPUiSto1k/KhrvqWrUSc2m5TXHjMWQ==",
+ "license": "CC0-1.0",
+ "dependencies": {
+ "eventsource-client": "^1.1.3"
+ }
+ },
"node_modules/sander": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
diff --git a/example-servers/sveltekit/package.json b/example-servers/sveltekit/package.json
index fa305ee11..b5a31409b 100644
--- a/example-servers/sveltekit/package.json
+++ b/example-servers/sveltekit/package.json
@@ -22,6 +22,7 @@
"type": "module",
"dependencies": {
"deep-chat": "^2.2.2",
- "eventsource-parser": "^1.1.2"
+ "eventsource-parser": "^1.1.2",
+ "salesforce-agent-api-client": "^1.0.1"
}
}
diff --git a/example-servers/sveltekit/src/routes/+page.svelte b/example-servers/sveltekit/src/routes/+page.svelte
index 77ea9479b..3d0326a8e 100644
--- a/example-servers/sveltekit/src/routes/+page.svelte
+++ b/example-servers/sveltekit/src/routes/+page.svelte
@@ -232,5 +232,32 @@
errorMessages={{displayServiceErrorMessages: true}}
/>
+
Server for Salesforce Agentforce
+
+
+
+ Make sure to set the SALESFORCE_INSTANCE_URL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET and SALESFORCE_AGENT_ID environment variable in your server
+
{/if}
diff --git a/example-servers/sveltekit/src/routes/api/salesforce/agentforce-stream/+server.ts b/example-servers/sveltekit/src/routes/api/salesforce/agentforce-stream/+server.ts
new file mode 100644
index 000000000..4869bd599
--- /dev/null
+++ b/example-servers/sveltekit/src/routes/api/salesforce/agentforce-stream/+server.ts
@@ -0,0 +1,128 @@
+import type { DeepChatTextRequestBody } from "../../../types/deepChatTextRequestBody";
+import type { RequestHandler } from "@sveltejs/kit";
+import AgentApiClient from "salesforce-agent-api-client";
+import { writable } from 'svelte/store';
+
+export const config = {
+ runtime: "edge",
+ // this is used to enable streaming
+ dynamic: 'force-dynamic'
+};
+
+// Make sure to set the SALESFORCE_INSTANCE_URL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET and SALESFORCE_AGENT_ID environment variable
+
+interface StreamEvent {
+ data: string;
+ event: string;
+}
+
+const salesforceSession = writable(null);
+let sessionStore: string | null = null;
+salesforceSession.subscribe(value => {
+ sessionStore = value;
+});
+
+export const POST: RequestHandler = async ({ request }) => {
+ // Load config from .env file
+ const config = {
+ instanceUrl: process.env.SALESFORCE_INSTANCE_URL || "",
+ clientId: process.env.SALESFORCE_CLIENT_ID || "",
+ clientSecret: process.env.SALESFORCE_CLIENT_SECRET || "",
+ agentId: process.env.SALESFORCE_AGENT_ID || "",
+ };
+
+ // Configure Agent API client
+ const client = new AgentApiClient(config);
+ const responseStream = new TransformStream();
+ const writer = responseStream.writable.getWriter();
+ const encoder = new TextEncoder();
+
+ try {
+ // Authenticate
+ await client.authenticate();
+
+ // Get the message from the request
+ const messageRequestBody = (await request.json()) as DeepChatTextRequestBody;
+ const message = messageRequestBody.messages[messageRequestBody.messages.length - 1].text;
+
+ // Validate that a message was provided in the request
+ if (!message) {
+ throw new Error("No message provided");
+ }
+
+ let sessionId: string;
+
+ // Session Management
+ if (sessionStore) {
+ sessionId = sessionStore;
+ } else {
+ sessionId = await client.createSession();
+ salesforceSession.set(sessionId);
+ }
+
+ // Stream event handler
+ function streamEventHandler({ data, event }: StreamEvent) {
+ const eventData = JSON.parse(data);
+ // console.log('Event:', event);
+
+ switch (event) {
+ case 'TEXT_CHUNK':
+ // Handle TEXT_CHUNK event
+ // Write the event data to the stream in Deep Chat format
+ console.log('TEXT_CHUNK:', eventData.message?.message);
+ writer.write(encoder.encode(`data: ${JSON.stringify({ text: eventData.message?.message || '' })}\n\n`));
+ break;
+ case 'END_OF_TURN':
+ // Handle END_OF_TURN event
+ writer.close();
+ break;
+ case 'INFORM':
+ // do nothing for INFORM event
+ break;
+ default:
+ console.log('Unknown event:', eventData);
+ }
+
+ }
+
+ // Stream disconnect handler
+ async function streamDisconnectHandler() {
+ if (!writer.closed) {
+ writer.close();
+ }
+ }
+
+ // Send the streaming message
+ client.sendStreamingMessage(
+ sessionId,
+ message,
+ [], // Empty array for variables/context
+ streamEventHandler,
+ streamDisconnectHandler
+ );
+
+ return new Response(responseStream.readable, {
+ headers: {
+ 'Content-Type': 'text/event-stream',
+ 'Connection': 'keep-alive',
+ 'Cache-Control': 'no-cache, no-transform',
+ },
+ });
+
+ } catch (error) {
+ console.error("Salesforce Agent API Error:", error);
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
+
+ // Write error to stream and close it
+ writer.write(encoder.encode(`data: ${JSON.stringify({ text: "Error: " + errorMessage })}\n\n`));
+ writer.close();
+
+ return new Response(responseStream.readable, {
+ headers: {
+ 'Content-Type': 'text/event-stream',
+ 'Connection': 'keep-alive',
+ 'Cache-Control': 'no-cache, no-transform',
+ },
+ });
+ }
+};
diff --git a/example-servers/sveltekit/src/routes/api/salesforce/agentforce/+server.ts b/example-servers/sveltekit/src/routes/api/salesforce/agentforce/+server.ts
new file mode 100644
index 000000000..51c409f13
--- /dev/null
+++ b/example-servers/sveltekit/src/routes/api/salesforce/agentforce/+server.ts
@@ -0,0 +1,89 @@
+import type { DeepChatTextRequestBody } from "../../../types/deepChatTextRequestBody";
+import type { RequestHandler } from "@sveltejs/kit";
+import AgentApiClient from "salesforce-agent-api-client";
+import { writable } from 'svelte/store';
+
+export const config = {
+ runtime: "edge",
+};
+
+// Make sure to set the SALESFORCE_INSTANCE_URL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET and SALESFORCE_AGENT_ID environment variable
+
+// Create a writable store to manage Salesforce session state
+// This store holds the session ID for maintaining conversation context
+// Initial value is null, indicating no active session
+const salesforceSession = writable(null);
+let sessionStore: string | null = null;
+salesforceSession.subscribe(value => {
+ sessionStore = value;
+});
+
+export const POST: RequestHandler = async ({ request }) => {
+ // Load config from env
+ const config = {
+ instanceUrl: process.env.SALESFORCE_INSTANCE_URL || "",
+ clientId: process.env.SALESFORCE_CLIENT_ID || "",
+ clientSecret: process.env.SALESFORCE_CLIENT_SECRET || "",
+ agentId: process.env.SALESFORCE_AGENT_ID || "",
+ };
+
+ // Configure Agent API client
+ const client = new AgentApiClient(config);
+
+ try {
+ // Authenticate
+ await client.authenticate();
+
+ // Get the message from the request
+ const messageRequestBody = (await request.json()) as DeepChatTextRequestBody;
+ const message = messageRequestBody.messages[messageRequestBody.messages.length - 1].text;
+
+ // Validate that a message was provided in the request
+ if (!message) {
+ throw new Error("No message provided");
+ }
+
+ let sessionId: string;
+
+ // Session Management:
+ // We either reuse an existing session from the store or create a new one.
+ // This helps maintain conversation context and reduces unnecessary session creation.
+ if (sessionStore) {
+ // Reuse the existing session ID from the store
+ sessionId = sessionStore;
+ } else {
+ // If no session exists, create a new one and store it
+ sessionId = await client.createSession();
+ salesforceSession.set(sessionId);
+ }
+
+ // Send the message to Salesforce Agent API and wait for the synchronous response
+ // The empty array parameter is for additional context that might be needed in future
+ const syncResponse = await client.sendSyncMessage(sessionId, message, []);
+
+ // Format and return the agent's response
+ // If no message is received, provide a fallback response
+ return new Response(JSON.stringify({
+ text: syncResponse.messages[0].message || "No response received from the Salesforce agent",
+ }), {
+ headers: { "content-type": "application/json" },
+ });
+
+ } catch (error) {
+ // Log the full error for debugging purposes
+ console.error("Salesforce Agent API Error:", error);
+ // Extract error message safely, providing a fallback for non-Error objects
+ const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
+ return new Response(
+ JSON.stringify({
+ text: "Error communicating with Salesforce Agent API: " + errorMessage,
+ }),
+ {
+ status: 500,
+ headers: {
+ "content-type": "application/json",
+ },
+ }
+ );
+ }
+};
diff --git a/website/static/img/salesforceLogo.png b/website/static/img/salesforceLogo.png
new file mode 100644
index 000000000..8aaef6686
Binary files /dev/null and b/website/static/img/salesforceLogo.png differ