diff --git a/packages/components/nodes/analytic/JLINC/JLINC.ts b/packages/components/nodes/analytic/JLINC/JLINC.ts index dbbd3099dd5..326ae6fa98f 100644 --- a/packages/components/nodes/analytic/JLINC/JLINC.ts +++ b/packages/components/nodes/analytic/JLINC/JLINC.ts @@ -17,7 +17,7 @@ class JLINC_Analytic implements INode { this.name = 'jlinc' this.version = 1.0 this.type = 'JLINC' - this.icon = 'jlinc.png' + this.icon = 'jlinc.svg' this.category = 'Analytic' this.baseClasses = [this.type] this.inputs = [] diff --git a/packages/components/nodes/analytic/JLINC/jlinc.png b/packages/components/nodes/analytic/JLINC/jlinc.png deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/components/nodes/analytic/JLINC/jlinc.svg b/packages/components/nodes/analytic/JLINC/jlinc.svg new file mode 100644 index 00000000000..4675e7809a3 --- /dev/null +++ b/packages/components/nodes/analytic/JLINC/jlinc.svg @@ -0,0 +1 @@ +JLINC Knot 60px RGB \ No newline at end of file diff --git a/packages/components/src/handler.ts b/packages/components/src/handler.ts index 5963464fe69..3fcd3d2b497 100644 --- a/packages/components/src/handler.ts +++ b/packages/components/src/handler.ts @@ -27,7 +27,7 @@ import { LunaryHandler } from '@langchain/community/callbacks/handlers/lunary' import { getCredentialData, getCredentialParam, getEnvironmentVariable } from './utils' import { ICommonObject, IDatabaseEntity, INodeData, IServerSideEventStreamer } from './Interface' import { LangWatch, LangWatchSpan, LangWatchTrace, autoconvertTypedValues } from 'langwatch' -import { CredentialInfo, extractCredentialsAndModels } from './flowCredentialExtractor' +import { extractCredentialsAndModels } from './flowCredentialExtractor' export interface TraceMetadata { stripeCustomerId: string subscriptionTier?: string @@ -75,6 +75,19 @@ function applyEnvAnalyticsOverrides(analyticConfig?: string | object): any { } } + // JLINC env override - takes precedence over UI configuration + if (process.env.JLINC_DATA_STORE_API_KEY) { + analytic.jlinc = { + status: true, + dataStoreApiUrl: process.env.JLINC_DATA_STORE_API_URL, + dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY, + archiveApiUrl: process.env.JLINC_ARCHIVE_API_URL, + archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY, + agreementId: process.env.JLINC_AGREEMENT_ID ?? '00000000-0000-0000-0000-000000000000', + systemPrefix: process.env.JLINC_SYSTEM_PREFIX + } + } + // Future providers can be added here: // if (process.env.LANGSMITH_API_KEY) { analytic.langSmith = { ... } } // if (process.env.ARIZE_API_KEY) { analytic.arize = { ... } } @@ -794,15 +807,17 @@ export const additionalCallbacks = async (nodeData: INodeData, options: ICommonO const tracer: Tracer | undefined = getOpikTracer(opikOptions) callbacks.push(tracer) } else if (provider === 'jlinc') { - const dataStoreApiUrl = getCredentialParam('dataStoreApiUrl', credentialData, nodeData) - const dataStoreApiKey = getCredentialParam('dataStoreApiKey', credentialData, nodeData) - const archiveApiUrl = getCredentialParam('archiveApiUrl', credentialData, nodeData) - const archiveApiKey = getCredentialParam('archiveApiKey', credentialData, nodeData) - let agreementId = analytic[provider].agreementId as string + const dataStoreApiUrl = + analytic[provider]?.dataStoreApiUrl ?? getCredentialParam('dataStoreApiUrl', credentialData, nodeData) + const dataStoreApiKey = + analytic[provider]?.dataStoreApiKey ?? getCredentialParam('dataStoreApiKey', credentialData, nodeData) + const archiveApiUrl = analytic[provider]?.archiveApiUrl ?? getCredentialParam('archiveApiUrl', credentialData, nodeData) + const archiveApiKey = analytic[provider]?.archiveApiKey ?? getCredentialParam('archiveApiKey', credentialData, nodeData) + let agreementId = analytic[provider]?.agreementId as string if (!agreementId || agreementId === '') { agreementId = '00000000-0000-0000-0000-000000000000' } - const systemPrefix = analytic[provider].systemPrefix as string + const systemPrefix = analytic[provider]?.systemPrefix as string const tracer = new JLINCTracer({ dataStoreApiUrl, dataStoreApiKey, diff --git a/packages/docs/src/pages/jlinc-partnership.tsx b/packages/docs/src/pages/jlinc-partnership.tsx index 084cb27f157..441a2816adf 100644 --- a/packages/docs/src/pages/jlinc-partnership.tsx +++ b/packages/docs/src/pages/jlinc-partnership.tsx @@ -3,7 +3,6 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext' import Layout from '@theme/Layout' import JsonLd from '@site/src/components/JsonLd' import ThreeJsScene from '@site/src/components/Annimations/SphereScene' -import UsingAnswerAgentAISubmenu from '@site/src/components/UsingAnswerAgentAISubmenu' import styles from './index.module.css' @@ -46,8 +45,8 @@ function PartnershipHero() { How It Works - - Technical Architecture + + JLINC Documentation @@ -79,23 +78,23 @@ function VisionSection() {
Why This Partnership Matters
-

Solving AI's Trust Gap

+

Solving AI's Trust Gap

As AI capabilities accelerate, a critical barrier emerges: How do you trust what AI produces? For - public companies and regulated industries, this isn't philosophical—it's existential. Boards won't approve - AI-assisted 10-Ks without provable lineage. Auditors won't sign off on AI-generated risk reports without - tamper-evident trails. Compliance officers won't deploy AI workflows that can't demonstrate complete chain of - custody. + public companies and regulated industries, this isn't philosophical—it's existential. Boards won't + approve AI-assisted 10-Ks without provable lineage. Auditors won't sign off on AI-generated risk reports + without tamper-evident trails. Compliance officers won't deploy AI workflows that can't demonstrate + complete chain of custody.

Answer Agents and JLINC are pioneering the solution.

- By combining Answer Agents' sophisticated AI orchestration with JLINC's cryptographic provenance protocol, we're - creating something unprecedented: AI workflows where every decision is traceable, every contribution is accountable, - and every output is auditable. + By combining Answer Agents' sophisticated AI orchestration with JLINC's cryptographic provenance protocol, + we're creating something unprecedented: AI workflows where every decision is traceable, every contribution is + accountable, and every output is auditable.

@@ -115,10 +114,10 @@ function VisionSection() {

JLINC Brings

@@ -147,21 +146,21 @@ function ProblemSection() { { icon: '📄', title: 'Where did this come from?', - pain: 'AI generates earnings reports mixing data from 15 sources. Auditor asks: "Prove these numbers came from approved systems."', + pain: 'AI generates earnings reports mixing data from 15 sources. Auditor asks: "Prove these numbers came from approved systems."', reality: 'Screenshots, log files, trust', problem: 'Not sufficient for SOX compliance' }, { icon: '👁️', title: 'What exactly did the AI see?', - pain: 'Agent analyzes confidential merger docs. Legal asks: "Show us exactly what context the AI had."', + pain: 'Agent analyzes confidential merger docs. Legal asks: "Show us exactly what context the AI had."', reality: 'Best-effort prompt logs, often incomplete', problem: 'Not admissible as evidence' }, { icon: '✍️', title: 'Who made the final decision?', - pain: 'AI drafts SEC filing, three executives review it. Board asks: "Who signed off on what?"', + pain: 'AI drafts SEC filing, three executives review it. Board asks: "Who signed off on what?"', reality: 'Email threads, comments in Google Docs', problem: 'Not audit-ready, no cryptographic proof' } @@ -235,7 +234,7 @@ function SolutionSection() { icon: '📄', title: 'Source Verification', subtitle: 'Ingested documents', - detail: 'SHA-256 hash of each document signed via JLINC agreement', + detail: 'SHA-256 hash of each document signed via JLINC protocol', result: 'Provable origin of all inputs' }, { @@ -272,7 +271,7 @@ function SolutionSection() {

Immutable Chain of Custody for AI Workflows

- Every step—from data ingestion to final report—is cryptographically stamped and anchored to an immutable ledger + Every step—from data ingestion to final report—is cryptographically signed and privately archived to ensure immutability

@@ -331,14 +330,6 @@ function SolutionSection() {
))} - -
- Cryptographic Foundation: Every event is signed using standard public-key cryptography and anchored to - your chosen ledger—whether that's a private database, Hyperledger, or public blockchain. -
) @@ -386,7 +377,7 @@ function ComparisonSection() { {[ ['Lineage Granularity', 'Best-effort prompt logs', 'Cryptographic event stream at context-window level'], - ['Proof Standard', 'Screenshot / PDF export', 'Ledger-backed, machine-verifiable receipts'], + ['Proof Standard', 'Screenshot / PDF export', 'Cryptographically-backed, machine-verifiable receipts'], [ 'Human ↔ AI Handshake', 'Informal comments or approval emails', @@ -404,7 +395,11 @@ function ComparisonSection() { 'Username/password authentication', 'Cryptographic public-key identity (non-repudiable)' ], - ['Ledger Flexibility', 'Proprietary databases', 'Vendor-neutral: private DB, Hyperledger, or public chain'] + [ + 'High-Volume Performance', + 'Limited by database performance', + 'Tens of millions of records per day in single cloud thread' + ] ].map((row, idx) => ( {row[0]} @@ -420,9 +415,9 @@ function ComparisonSection() { className={clsx(styles.testimonialCard)} style={{ marginTop: '2rem', maxWidth: '900px', marginLeft: 'auto', marginRight: 'auto', textAlign: 'center' }} > - This isn't just better logging—it's a{' '} - fundamental shift in how AI systems prove their trustworthiness. From "we - logged it" to "it's cryptographically impossible to falsify." + This isn't just better logging—it's a{' '} + fundamental shift in how AI systems prove their trustworthiness. From + "we logged it" to "it's cryptographically impossible to falsify." @@ -472,7 +467,7 @@ function BenefitsSection() { title: 'Scale with Confidence', subtitle: 'For Executives', description: 'Board members can trust AI-assisted strategic documents. Scale agent workflows without sacrificing governance.', - detail: "Competitive advantage: deploy AI where others can't" + detail: 'Competitive advantage: deploy AI where others can't' } ] @@ -567,7 +562,7 @@ function IndustryUseCases() {
Industry Use Cases
-

Built for Enterprises That Can't Afford to Guess

+

Built for Enterprises That Can't Afford to Guess

{industries.map((industry, idx) => ( @@ -619,6 +614,8 @@ function IndustryUseCases() { ) } +// Unused function - commented out to fix ESLint error +// eslint-disable-next-line unused-imports/no-unused-vars function TechnicalArchitecture() { return (
@@ -628,7 +625,7 @@ function TechnicalArchitecture() {

Protocol-Based, Not Platform-Locked

- JLINC's open standard integrates seamlessly with Answer Agents' orchestration layer + JLINC's open standard integrates seamlessly with Answer Agents' orchestration layer

About JLINC
-

Powered by JLINC's Open Provenance Protocol

+

Powered by JLINC's Open Provenance Protocol

@@ -782,8 +779,9 @@ function AboutJLINCSection() {

Protocol, Not Platform

- JLINC isn't a blockchain company or SaaS vendor. It's an open protocol specification that - works with any infrastructure. Think of it like SMTP for provenance—a standard anyone can implement. + JLINC isn't a blockchain company or SaaS vendor. It's an{' '} + open protocol specification that works with any infrastructure. Think of it like SMTP for + provenance—a standard anyone can implement.

@@ -791,8 +789,8 @@ function AboutJLINCSection() {

Patent-Protected Innovation

- US Patent "Internet Data Usage Control Systems" issued January 17, 2023 by the US Patent and Trademark - Office. Legally protected intellectual property with nearly a decade of R&D. + US Patent "Internet Data Usage Control Systems" issued January 17, 2023 by the US Patent and + Trademark Office. Legally protected intellectual property with nearly a decade of R&D.

@@ -829,21 +827,20 @@ function AboutJLINCSection() { flexWrap: 'wrap' }} > - - Learn more about JLINC + + JLINC Documentation JLINC Protocol Specification - - W3C DID Method Spec - ) } +// Unused function - commented out to fix ESLint error +// eslint-disable-next-line unused-imports/no-unused-vars function ImplementationRoadmap() { const phases = [ { @@ -966,6 +963,8 @@ function ImplementationRoadmap() { ) } +// Unused function - commented out to fix ESLint error +// eslint-disable-next-line unused-imports/no-unused-vars function PartnershipVisionSection() { return (
@@ -982,11 +981,11 @@ function PartnershipVisionSection() {

As AI becomes more capable—from writing code to making medical diagnoses to managing investment portfolios—the need - for provenance becomes existential. Society can't afford opaque AI systems in critical domains. We need verifiable - intelligence. + for provenance becomes existential. Society can't afford opaque AI systems in critical domains. We need + verifiable intelligence.

- That's what we're building together. + That's what we're building together.

@@ -1045,31 +1044,104 @@ function PartnershipVisionSection() { ) } +function ZeroKnowledgeAuditingSection() { + return ( +
+
+
+ Core Capability +
+

Truly Private, Zero-Knowledge, Third-Party Auditing

+

+ Enable external auditors to verify your AI operations without ever seeing your private data +

+ +
+

+ Traditional auditing requires sharing sensitive data with auditors. JLINC's zero-knowledge approach + fundamentally changes this—enabling cryptographic verification of compliance without exposing your proprietary + information. +

+
+ +
+
+
+
🔐
+

Cryptographic Signing

+

+ Every time an event passes through the system, it is cryptographically signed by that element of the system, + creating an immutable chain of accountability. +

+
+
+
+
+
🔒
+

Privatizing Hash Algorithm

+

+ Data is passed through a privatizing hashing algorithm to create zero-knowledge audit records—one-way + functions that can't be reverse-engineered to reveal sensitive content. +

+
+
+
+ +
+
+
+
📦
+

Third-Party Archiving

+

+ Audit records are delivered to a third-party archiver for use in later audits, maintaining independence and + preventing tampering by any single party. +

+
+
+
+
+
✓
+

Verifiable Compliance

+

+ External auditors can cryptographically verify that data and ownership is what you claim it to be—without + ever being given access to your private data. +

+
+
+
+ +
+ + This enables businesses to leverage third-party auditors that can verify compliance without compromising data + privacy. + + Auditors prove what happened. You keep your data. Everyone wins. +
+
+
+ ) +} + function FAQSection() { const faqs = [ { q: 'How is this different from blockchain-based AI solutions?', - a: 'JLINC is a protocol, not a blockchain. It can anchor to private databases, Hyperledger, or public blockchains—giving you maximum flexibility. Most "blockchain AI" solutions lock you into specific infrastructure. JLINC adapts to yours.' + a: 'JLINC is a protocol, not a blockchain. It works with high-volume, high-speed infrastructure without the overhead of traditional ledgers. Process tens of millions of records per day in a single cloud thread—impossible with blockchain-based solutions.' }, { q: 'Does cryptographic verification slow down AI workflows?', - a: 'No. JLINC stamping happens asynchronously, adding <100ms per event (imperceptible to users). Your agents run at full speed while provenance is captured in the background.' - }, - { - q: "What if we don't want to use a public blockchain?", - a: "You don't have to. JLINC is vendor-neutral. Anchor to your private PostgreSQL database, Hyperledger Fabric, or any system you trust. The cryptographic signatures provide tamper-evidence regardless of ledger choice." + a: 'No. JLINC stamping happens asynchronously, adding <100ms per event (imperceptible to users). Your agents run at full speed while provenance is captured in the background. The non-blockchain architecture enables processing tens of millions of records daily without performance degradation.' }, { q: 'Can we verify provenance years later?', - a: "Yes. JLINC creates immutable records that can be queried indefinitely. As long as the ledger exists (your responsibility to maintain), provenance is verifiable. Think of it like digital signatures on PDFs—they don't expire." - }, - { - q: 'How do you protect data privacy?', - a: "Only cryptographic hashes are written to the ledger—never the actual data. Hashes are one-way functions (can't reverse-engineer content). Your sensitive information stays in your systems with your access controls." + a: 'Yes. JLINC creates immutable records that can be queried indefinitely. As long as the archive exists (your responsibility to maintain), provenance is verifiable. Think of it like digital signatures on PDFs—they don't expire.' }, { q: 'What happens if Answer Agents or JLINC goes out of business?', - a: "JLINC is an open protocol. The signature verification doesn't depend on JLINC-the-company existing. As long as you have the cryptographic keys and ledger data, you can verify provenance. We can also provide code escrow arrangements." + a: 'JLINC is an open protocol. The signature verification doesn't depend on JLINC-the-company existing. As long as you have the cryptographic keys and archive data, you can verify provenance. We can also provide code escrow arrangements.' } ] @@ -1103,7 +1175,7 @@ function DemoCTASection() {

Experience Verifiable AI in Action

- See how Answer Agents + JLINC transforms AI workflows from "trust us" to "verify yourself" + See how Answer Agents + JLINC transforms AI workflows from "trust us" to "verify yourself"

@@ -1112,7 +1184,7 @@ function DemoCTASection() { style={{ background: 'rgba(0, 255, 255, 0.08)', border: '2px solid rgba(0, 255, 255, 0.35)' }} >

Schedule 30-Minute Demo

-

What you'll see:

+

What you'll see:

@@ -1191,6 +1269,8 @@ function DemoCTASection() { ) } +// Unused function - commented out to fix ESLint error +// eslint-disable-next-line unused-imports/no-unused-vars function CoFooter() { return (
@@ -1260,6 +1340,8 @@ function CoFooter() { } export default function JLINCPartnership(): JSX.Element { + // siteConfig unused but required by useDocusaurusContext hook + // eslint-disable-next-line unused-imports/no-unused-vars const { siteConfig } = useDocusaurusContext() return ( @@ -1292,6 +1374,7 @@ export default function JLINCPartnership(): JSX.Element { + {/* */} diff --git a/packages/ui/src/assets/images/jlinc.png b/packages/ui/src/assets/images/jlinc.png deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/ui/src/assets/images/jlinc.svg b/packages/ui/src/assets/images/jlinc.svg new file mode 100644 index 00000000000..4675e7809a3 --- /dev/null +++ b/packages/ui/src/assets/images/jlinc.svg @@ -0,0 +1 @@ +JLINC Knot 60px RGB \ No newline at end of file diff --git a/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx b/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx index c67517bb634..8be9cc94537 100644 --- a/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx +++ b/packages/ui/src/ui-component/dialog/ChatflowConfigurationDialog.jsx @@ -15,6 +15,7 @@ import ChatLinksSettings from '@/ui-component/extended/ChatLinksSettings' import FileUpload from '@/ui-component/extended/FileUpload' import PostProcessing from '@/ui-component/extended/PostProcessing' import Security from '@/ui-component/extended/Security' +import JlincSettings from '@/ui-component/extended/JlincSettings' // import AnalyseFlow from '@/ui-component/extended/AnalyseFlow' const CHATFLOW_CONFIGURATION_TABS = [ @@ -26,6 +27,10 @@ const CHATFLOW_CONFIGURATION_TABS = [ label: 'Security', id: 'security' }, + { + label: 'Audit Log', + id: 'jlinc' + }, { label: 'Visibility', id: 'visibilitySettings' @@ -166,6 +171,7 @@ const ChatflowConfigurationDialog = ({ show, isAgentCanvas, dialogProps, onCance {item.id === 'fileUpload' ? : null} {item.id === 'postProcessing' ? : null} {item.id === 'security' ? : null} + {item.id === 'jlinc' ? : null} ))} diff --git a/packages/ui/src/ui-component/extended/AnalyseFlow.jsx b/packages/ui/src/ui-component/extended/AnalyseFlow.jsx index c3b84ad387d..10af1dd5d20 100644 --- a/packages/ui/src/ui-component/extended/AnalyseFlow.jsx +++ b/packages/ui/src/ui-component/extended/AnalyseFlow.jsx @@ -25,7 +25,7 @@ import { SwitchInput } from '@/ui-component/switch/Switch' import { Input } from '@/ui-component/input/Input' import { StyledButton } from '@/ui-component/button/StyledButton' import langsmithPNG from '@/assets/images/langchain.png' -import jlincPNG from '@/assets/images/jlinc.png' +import jlincPNG from '@/assets/images/jlinc.svg' import langfuseSVG from '@/assets/images/langfuse.svg' import lunarySVG from '@/assets/images/lunary.svg' import langwatchSVG from '@/assets/images/langwatch.svg' diff --git a/packages/ui/src/ui-component/extended/JlincSettings.jsx b/packages/ui/src/ui-component/extended/JlincSettings.jsx new file mode 100644 index 00000000000..3515a5b4c1a --- /dev/null +++ b/packages/ui/src/ui-component/extended/JlincSettings.jsx @@ -0,0 +1,274 @@ +import { useDispatch } from 'react-redux' +import { useState, useEffect } from 'react' +import PropTypes from 'prop-types' +import { enqueueSnackbar as enqueueSnackbarAction, closeSnackbar as closeSnackbarAction, SET_CHATFLOW } from '@/store/actions' + +// material-ui +import { + Typography, + Box, + Button, + Accordion, + AccordionSummary, + AccordionDetails, + ListItem, + ListItemAvatar, + ListItemText +} from '@mui/material' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import { IconX } from '@tabler/icons-react' + +// Project import +import CredentialInputHandler from '@/views/canvas/CredentialInputHandler' +import { TooltipWithParser } from '@/ui-component/tooltip/TooltipWithParser' +import { SwitchInput } from '@/ui-component/switch/Switch' +import { Input } from '@/ui-component/input/Input' +import { StyledButton } from '@/ui-component/button/StyledButton' +import jlincPNG from '@/assets/images/jlinc.svg' + +// store +import useNotifier from '@/utils/useNotifier' + +// API +import chatflowsApi from '@/api/chatflows' +import Image from 'next/image' + +const JlincSettings = ({ dialogProps }) => { + const dispatch = useDispatch() + + useNotifier() + + const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) + const closeSnackbar = (...args) => dispatch(closeSnackbarAction(...args)) + + const [analytic, setAnalytic] = useState({}) + const [expanded, setExpanded] = useState(true) + + const jlincProvider = { + label: 'JLINC', + name: 'jlinc', + icon: jlincPNG, + url: 'https://jlinc.com', + description: 'Auditable trace logging for compliance and governance', + inputs: [ + { + label: 'Connect Credential', + name: 'credential', + type: 'credential', + credentialNames: ['jlincApi'] + }, + { + label: 'Agreement ID', + name: 'agreementId', + type: 'string', + optional: true, + description: 'The UUID of the agreement that transactions occur under', + placeholder: '00000000-0000-0000-0000-000000000000' + }, + { + label: 'System Prefix', + name: 'systemPrefix', + type: 'string', + optional: false, + description: 'The prefix to give DID user shortnames', + placeholder: 'MyProject' + }, + { + label: 'On/Off', + name: 'status', + type: 'boolean', + optional: true + } + ] + } + + const onSave = async () => { + try { + const saveResp = await chatflowsApi.updateChatflow(dialogProps.chatflow.id, { + analytic: JSON.stringify(analytic) + }) + if (saveResp.data) { + enqueueSnackbar({ + message: 'JLINC Configuration Saved', + options: { + key: new Date().getTime() + Math.random(), + variant: 'success', + action: (key) => ( + + ) + } + }) + dispatch({ type: SET_CHATFLOW, chatflow: saveResp.data }) + } + } catch (error) { + enqueueSnackbar({ + message: `Failed to save JLINC Configuration: ${ + typeof error.response.data === 'object' ? error.response.data.message : error.response.data + }`, + options: { + key: new Date().getTime() + Math.random(), + variant: 'error', + persist: true, + action: (key) => ( + + ) + } + }) + } + } + + const setValue = (value, inputParamName) => { + let newVal = { ...analytic } + if (!Object.prototype.hasOwnProperty.call(analytic, 'jlinc')) { + newVal.jlinc = {} + } + newVal.jlinc[inputParamName] = value + setAnalytic(newVal) + } + + const handleAccordionChange = (event, isExpanded) => { + setExpanded(isExpanded) + } + + useEffect(() => { + if (dialogProps.chatflow && dialogProps.chatflow.analytic) { + try { + const parsed = JSON.parse(dialogProps.chatflow.analytic) + // Load entire analytic config to preserve other providers (Langfuse, LangSmith, etc.) + setAnalytic(parsed || {}) + } catch (e) { + setAnalytic({}) + console.error(e) + } + } + + return () => { + setAnalytic({}) + setExpanded(true) + } + }, [dialogProps]) + + return ( + <> + + + {jlincProvider.description} + + + Note: Environment variables (JLINC_*) will override these settings if configured. + + + + } aria-controls='jlinc-content' id='jlinc-header'> + + +
+ JLINC +
+
+ + {jlincProvider.url} + + } + /> + {analytic.jlinc && analytic.jlinc.status && ( +
+
+ ON +
+ )} + + + + {jlincProvider.inputs.map((inputParam, index) => ( + +
+ + {inputParam.label} + {!inputParam.optional &&  *} + {inputParam.description && ( + + )} + +
+ {expanded && inputParam.type === 'credential' && ( + setValue(newValue, 'credentialId')} + /> + )} + {expanded && inputParam.type === 'boolean' && ( + setValue(newValue, inputParam.name)} + value={analytic.jlinc ? analytic.jlinc[inputParam.name] : inputParam.default ?? false} + /> + )} + {expanded && + (inputParam.type === 'string' || inputParam.type === 'password' || inputParam.type === 'number') && ( + setValue(newValue, inputParam.name)} + value={analytic.jlinc ? analytic.jlinc[inputParam.name] : inputParam.default ?? ''} + /> + )} +
+ ))} +
+ + + Save + + + ) +} + +JlincSettings.propTypes = { + dialogProps: PropTypes.object +} + +export default JlincSettings