Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2589dba
change library uuid generate v4 for migrations postgresql
Bryansss1 Apr 24, 2026
4735711
Add logotipo SVGs and update components
Maggnoone Apr 27, 2026
8b065ec
Update package dependencies and TypeScript configuration
leomerida15 Apr 28, 2026
aa810f5
Add OpenCode package configuration
leomerida15 Apr 28, 2026
f63c445
add agents
leomerida15 Apr 28, 2026
7186e3e
Add comprehensive documentation for Alejandria and Flowise node
leomerida15 Apr 29, 2026
2a2afce
Add skill registry for Flow-stable project
leomerida15 Apr 29, 2026
3e2fc1f
Comment out GitHubStarButton in Header
Maggnoone Apr 29, 2026
bfce4df
Cambio de Instrumento: (Recomendación) Actualizar la descripción de la
leomerida15 Apr 29, 2026
4593bad
Update .prettierignore
leomerida15 Apr 29, 2026
e553a20
Update index.html
Maggnoone Apr 29, 2026
59fe3e7
Merge branch 'main' of https://github.com/testeos-z/Flow-stable
Maggnoone Apr 29, 2026
5539e5b
add skills and promps
leomerida15 Apr 30, 2026
aeb81e6
feat: add flowise-ai-mcp-server package as regular folder
leomerida15 Apr 30, 2026
beb3571
feat: add agent specialist skills, schemas, scripts and mcp-server en…
leomerida15 May 2, 2026
62b6c49
agent flow
leomerida15 May 4, 2026
f796691
ayuda
leomerida15 May 5, 2026
f635c39
feat(config): add testman agent and validation improvements
leomerida15 May 5, 2026
bdf2ea5
chore: add untracked project files
leomerida15 May 5, 2026
cae55da
sync engram memories
leomerida15 May 6, 2026
b4163b5
feat(a2a): add A2AStorageAdapter interface and Zod schemas
leomerida15 May 6, 2026
a5a9893
feat(a2a): add storage backends with LocalJsonAdapter and contract tests
leomerida15 May 6, 2026
2fc3f24
feat(a2a): add Registry and Task nodes
leomerida15 May 6, 2026
06a3a5d
feat(a2a): add Artifact and SharedContext nodes
leomerida15 May 6, 2026
6797868
feat(a2a): add MemoryAdapter node and integration tests
leomerida15 May 6, 2026
226161d
docs(a2a): add documentation, schemas reference, and Supabase DDL
leomerida15 May 6, 2026
7515f78
fix(a2a): address testman CRITICAL and WARNINGS
leomerida15 May 6, 2026
19df7df
fix(a2a): close remaining testman issues
leomerida15 May 6, 2026
582332f
feat(a2a): implement SQLite, Postgres, and Supabase adapters
leomerida15 May 6, 2026
3a1853b
merge: A2A Protocol Nodes for Flowise
leomerida15 May 6, 2026
9e413cd
docs(a2a): add agent templates and skill updates for flow-* agents
leomerida15 May 6, 2026
e5666d1
fix(a2a): add A2A nodes to allowlist and version manifest
leomerida15 May 6, 2026
aec24f3
chore: track .atl/skill-registry.md, remove from gitignore
leomerida15 May 7, 2026
6027729
fix(docker): use COPY --chown to avoid recursive chown timeout
leomerida15 May 7, 2026
3b8eb20
feat(a2a): implement LocalJsonAdapter with real file-based JSON storage
leomerida15 May 7, 2026
fa69595
feat(a2a): implement SQLiteAdapter with better-sqlite3
leomerida15 May 7, 2026
f65723b
feat(a2a-storage): implement PostgresAdapter with pg Pool and JSONB c…
leomerida15 May 7, 2026
aa11cd9
feat(a2a-storage): implement SupabaseAdapter with real @supabase/supa…
leomerida15 May 7, 2026
069fc4e
Merge pull request #2 from testeos-z/feat/a2a-localjson-adapter
leomerida15 May 7, 2026
cad232e
Merge pull request #4 from testeos-z/feat/a2a-postgres-adapter
leomerida15 May 7, 2026
76792a7
Merge pull request #5 from testeos-z/feat/a2a-supabase-adapter
leomerida15 May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
268 changes: 268 additions & 0 deletions .agents/flowise-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
/**
* Flowise API Integration Module
*
* Connects the testing pipeline to Flowise server for:
* - Creating temporary flows
* - Running predictions (smoke tests)
* - Deleting temporary flows
*/

import { IReactFlowObject } from '../schemas/flow-data'

const FLOWISE_BASE_URL = process.env.FLOWISE_API_URL || 'http://localhost:3000'

interface FlowiseChatflow {
id: string
name: string
flowData: string // JSON string
deployed: boolean
isPublic: boolean
apikeyid: string
chatbotConfig: string
createdDate: string
updatedDate: string
category: string | null
description: string | null
}

interface PredictionRequest {
question: string
history: Array<{ message: string; type: 'apiMessage' | 'userMessage' }>
overrideConfig?: Record<string, any>
}

interface PredictionResponse {
text?: string
json?: any
error?: string
sourceDocuments?: any[]
}

// ============================================================================
// Chatflow CRUD Operations
// ============================================================================

export async function createTempFlow(flowData: IReactFlowObject, name: string = `Test Flow ${Date.now()}`): Promise<string> {
const response = await fetch(`${FLOWISE_BASE_URL}/api/v1/chatflow`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name,
flowData: JSON.stringify(flowData),
deployed: false,
isPublic: false
})
})

if (!response.ok) {
const error = await response.text()
throw new Error(`Failed to create temp flow: ${error}`)
}

const result = await response.json()
return result.id
}

export async function deleteTempFlow(flowId: string): Promise<void> {
const response = await fetch(`${FLOWISE_BASE_URL}/api/v1/chatflow/${flowId}`, {
method: 'DELETE'
})

if (!response.ok) {
console.warn(`Failed to delete temp flow ${flowId}: ${await response.text()}`)
}
}

export async function getChatflow(flowId: string): Promise<FlowiseChatflow | null> {
try {
const response = await fetch(`${FLOWISE_BASE_URL}/api/v1/chatflow/${flowId}`)
if (!response.ok) return null
return await response.json()
} catch {
return null
}
}

// ============================================================================
// Prediction / Smoke Test
// ============================================================================

export async function runPrediction(
flowId: string,
question: string,
options: { timeout?: number } = {}
): Promise<{ success: boolean; response?: PredictionResponse; error?: string }> {
const timeout = options.timeout || 30000

try {
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), timeout)

const response = await fetch(`${FLOWISE_BASE_URL}/api/v1/prediction/${flowId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
question,
history: []
} as PredictionRequest),
signal: controller.signal
})

clearTimeout(timeoutId)

if (!response.ok) {
const errorText = await response.text()
return {
success: false,
error: `HTTP ${response.status}: ${errorText}`
}
}

const data = await response.json()

// Check for execution errors in response
if (data.error || (data.text && data.text.includes('Error:'))) {
return {
success: false,
error: data.error || data.text,
response: data
}
}

return {
success: true,
response: data
}
} catch (error: any) {
if (error.name === 'AbortError') {
return {
success: false,
error: `Prediction timed out after ${timeout}ms`
}
}

return {
success: false,
error: error.message
}
}
}

// ============================================================================
// Full Pipeline Integration
// ============================================================================

export interface SmokeTestResult {
passed: boolean
error?: string
response?: string
durationMs: number
}

export interface IntegrationTestResult {
passed: boolean
error?: string
toolCalls?: string[]
durationMs: number
}

/**
* Run smoke test: create flow, ask "Hello", verify response
*/
export async function runSmokeTest(
flowData: IReactFlowObject,
options: { cleanup?: boolean } = {}
): Promise<{ flowId: string; result: SmokeTestResult }> {
const startTime = Date.now()
let flowId: string

try {
// Create temporary flow
flowId = await createTempFlow(flowData, `Smoke Test ${Date.now()}`)

// Run prediction
const prediction = await runPrediction(flowId, 'Hello, are you working?')

const durationMs = Date.now() - startTime

if (!prediction.success) {
return {
flowId,
result: {
passed: false,
error: prediction.error,
durationMs
}
}
}

return {
flowId,
result: {
passed: true,
response: prediction.response?.text || JSON.stringify(prediction.response),
durationMs
}
}
} catch (error: any) {
return {
flowId: '',
result: {
passed: false,
error: error.message,
durationMs: Date.now() - startTime
}
}
} finally {
if (options.cleanup !== false && flowId!) {
await deleteTempFlow(flowId).catch(() => {})
}
}
}

/**
* Run integration test: force tool invocation
*/
export async function runIntegrationTest(flowId: string, flowHasTools: boolean): Promise<IntegrationTestResult> {
if (!flowHasTools) {
return {
passed: true,
toolCalls: [],
durationMs: 0
}
}

const startTime = Date.now()

try {
// Ask a question that should trigger tool usage
const prediction = await runPrediction(flowId, 'Search for information about New York City and tell me what you find.', {
timeout: 60000
})

const durationMs = Date.now() - startTime

if (!prediction.success) {
return {
passed: false,
error: prediction.error,
durationMs
}
}

// Check if sourceDocuments exist (indicates tool/retriever was used)
const hasToolResults = prediction.response?.sourceDocuments && prediction.response.sourceDocuments.length > 0

return {
passed: prediction.success,
toolCalls: hasToolResults ? ['retriever'] : [],
durationMs
}
} catch (error: any) {
return {
passed: false,
error: error.message,
durationMs: Date.now() - startTime
}
}
}
Loading