From af7704ecffa389a86813334cdb91f641eb9cbe43 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Fri, 24 Oct 2025 16:50:17 +0100 Subject: [PATCH 01/18] clean dto and files --- packages/agent/src/agents/index.ts | 5 - .../interfaces/agent.interface.ts} | 0 .../src/common/constant/agents.constants.ts | 2 +- .../common/constant/default-agent.constant.ts | 2 +- .../{agents.dto.ts => agent/config.dto.ts} | 104 +--- .../src/common/server/dto/agent/mcp.dto.ts | 133 +++++ .../common/server/dto/agent/message.dto.ts | 94 +++ packages/core/src/common/server/dto/agents.ts | 358 ------------ packages/core/src/common/server/dto/index.ts | 4 + .../core/src/common/server/dto/websocket.ts | 47 -- .../src/common/server/interfaces/agents.ts | 0 packages/core/src/config/database.config.ts | 2 +- packages/core/src/index.ts | 7 +- .../src/services/agent-validation.service.ts | 2 +- packages/server/mcp.txt | 534 ------------------ .../src/controllers/agents.controller.ts | 6 +- .../src/controllers/gateway.controller.ts | 56 +- .../src/interfaces/agent-service.interface.ts | 16 - .../server/src/interfaces/agent.interface.ts | 20 - .../server/src/interfaces/sql_interfaces.ts | 14 - .../interfaces/wallet-service.inferface.ts | 34 -- packages/server/src/services/agent.service.ts | 43 +- tests/src/types.ts | 2 +- 23 files changed, 263 insertions(+), 1222 deletions(-) rename packages/core/src/common/{agent.ts => agent/interfaces/agent.interface.ts} (100%) rename packages/core/src/common/server/dto/{agents.dto.ts => agent/config.dto.ts} (56%) create mode 100644 packages/core/src/common/server/dto/agent/mcp.dto.ts create mode 100644 packages/core/src/common/server/dto/agent/message.dto.ts delete mode 100644 packages/core/src/common/server/dto/agents.ts create mode 100644 packages/core/src/common/server/dto/index.ts delete mode 100644 packages/core/src/common/server/dto/websocket.ts delete mode 100644 packages/core/src/common/server/interfaces/agents.ts delete mode 100644 packages/server/mcp.txt delete mode 100644 packages/server/src/interfaces/agent.interface.ts delete mode 100644 packages/server/src/interfaces/sql_interfaces.ts delete mode 100644 packages/server/src/interfaces/wallet-service.inferface.ts diff --git a/packages/agent/src/agents/index.ts b/packages/agent/src/agents/index.ts index 4cf8cd697..2a5fd72dc 100644 --- a/packages/agent/src/agents/index.ts +++ b/packages/agent/src/agents/index.ts @@ -1,8 +1,3 @@ -export interface MessageRequest { - agent_id: string; - user_request: string | null; -} - export interface Message { agent_id: string; user_request: string; diff --git a/packages/core/src/common/agent.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts similarity index 100% rename from packages/core/src/common/agent.ts rename to packages/core/src/common/agent/interfaces/agent.interface.ts diff --git a/packages/core/src/common/constant/agents.constants.ts b/packages/core/src/common/constant/agents.constants.ts index 353604074..b40d65720 100644 --- a/packages/core/src/common/constant/agents.constants.ts +++ b/packages/core/src/common/constant/agents.constants.ts @@ -1,4 +1,4 @@ -import { AgentConfig, MemoryStrategy } from '@common/agent.js'; +import { AgentConfig, MemoryStrategy } from '@common/agent/interfaces/agent.interface.js'; /** * Agent Selector Configuration diff --git a/packages/core/src/common/constant/default-agent.constant.ts b/packages/core/src/common/constant/default-agent.constant.ts index 3b59a8514..5c097085a 100644 --- a/packages/core/src/common/constant/default-agent.constant.ts +++ b/packages/core/src/common/constant/default-agent.constant.ts @@ -1,4 +1,4 @@ -import { AgentConfig, MemoryStrategy, ModelConfig } from '@common/agent.js'; +import { AgentConfig, MemoryStrategy, ModelConfig } from '@common/agent/interfaces/agent.interface.js'; export const DEFAULT_AGENT_MODEL: ModelConfig = { provider: 'gemini', diff --git a/packages/core/src/common/server/dto/agents.dto.ts b/packages/core/src/common/server/dto/agent/config.dto.ts similarity index 56% rename from packages/core/src/common/server/dto/agents.dto.ts rename to packages/core/src/common/server/dto/agent/config.dto.ts index 34b03b65f..7580f1e08 100644 --- a/packages/core/src/common/server/dto/agents.dto.ts +++ b/packages/core/src/common/server/dto/agent/config.dto.ts @@ -1,5 +1,4 @@ -import { isNull } from 'util'; -import { AgentConfig } from '../../agent.js'; +import { AgentConfig } from '../../../agent/interfaces/agent.interface.js'; import { IsNotEmpty, IsOptional, @@ -24,46 +23,6 @@ export class AddAgentRequestDTO { agent: AgentConfig.Input; } -/** - * DTO for retrieving messages from a specific agent - */ -export class MessageFromAgentIdDTO { - @IsNotEmpty() - @IsString() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - thread_id: string; - - @IsOptional() - @IsInt() - @Min(1) - @Max(100) - limit_message?: number; -} - -/** - * Interface for message requests to agents - */ -export class MessageRequest { - @IsNotEmpty() - @IsString() - @IsUUID() - agent_id: string; - - @IsString() - @Length(1, 10000) - request: string; - - @IsOptional() - @IsInt() - @Min(0) - @Max(1) - hitl_threshold?: number; -} - /** * DTO for deleting multiple agents */ @@ -108,67 +67,6 @@ export class UpdateModelConfigDTO { maxTokens: number; } -export class Message { - @IsOptional() - @IsString() - @IsUUID() - agent_id?: string; - - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^[a-zA-Z_]+$/, { - message: 'Sender type must contain only letters and underscores', - }) - sender_type: string; - - @IsOptional() - @IsNotEmpty() - @IsString() - @Length(1, 10000) - content?: string; - - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^[a-zA-Z_]+$/, { - message: 'Status must contain only letters and underscores', - }) - status: string; -} - -export class AgentRequestDTO { - @IsNotEmpty() - request: Message; -} - -export class SupervisorRequest { - @IsNotEmpty() - @IsString() - @Length(1, 10000) - content: string; - - @IsOptional() - @IsString() - @IsUUID() - agentId?: string; // Optional: specify which agent to use -} - -export class SupervisorRequestDTO { - @IsNotEmpty() - request: SupervisorRequest; -} - -export class getMessagesFromAgentsDTO { - @IsNotEmpty() - @IsString() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - thread_id: string; -} export class InitializesRequestDTO { @IsArray() @ArrayNotEmpty() diff --git a/packages/core/src/common/server/dto/agent/mcp.dto.ts b/packages/core/src/common/server/dto/agent/mcp.dto.ts new file mode 100644 index 000000000..8d612d759 --- /dev/null +++ b/packages/core/src/common/server/dto/agent/mcp.dto.ts @@ -0,0 +1,133 @@ +import { + IsNotEmpty, + IsString, + IsUUID, + IsArray, + ArrayNotEmpty, +} from 'class-validator'; + +/** + * Request to get a specific agent's MCP config + */ +export class GetAgentMcpsRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; +} + +/** + * Request to get all MCP server of a specific agent + */ +export class AgentMCPRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + mcp_id: string; +} + +/** + * Request to update the value of one secret in a given MCP server + */ +export class UpdateMcpEnvValueRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + mcp_id: string; + + @IsNotEmpty() + @IsString() + secret_name: string; + + @IsNotEmpty() + @IsString() + secret_value: string; +} + +/** + * Request to rename a secret key in an MCP config + */ +export class UpdateMcpEnvNameRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + mcp_id: string; + + @IsNotEmpty() + @IsString() + old_name: string; + + @IsNotEmpty() + @IsString() + new_name: string; +} + +/** + * Request to replace --key value or --profile with a new value + */ +export class UpdateMcpValueRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + mcp_id: string; + + @IsNotEmpty() + @IsString() + new_value: string; +} + +/** + * Request to delete multiple MCP servers + */ +export class DeleteMultipleMcpServersRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsArray() + @ArrayNotEmpty() + mcp_ids: string[]; +} + +/** + * Request to add one or more new MCP servers + */ +export class AddMcpServerRequestDTO { + @IsNotEmpty() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + mcpServers: Record< + string, + { + command: string; + args?: string[] | string; + env?: Record; + [key: string]: any; + } + >; +} + +/** + * Request to update an entire agent's MCP server object + */ +export class UpdateAgentMcpDTO { + @IsNotEmpty() + @IsUUID() + id: string; + + @IsNotEmpty() + mcp_servers: Record; // Replace Record +} diff --git a/packages/core/src/common/server/dto/agent/message.dto.ts b/packages/core/src/common/server/dto/agent/message.dto.ts new file mode 100644 index 000000000..0cfec17a2 --- /dev/null +++ b/packages/core/src/common/server/dto/agent/message.dto.ts @@ -0,0 +1,94 @@ +import { + IsNotEmpty, + IsOptional, + IsString, + IsUUID, + Length, + Min, + Max, + IsInt, +} from 'class-validator'; + +/** + * DTO for retrieving messages from a specific agent + */ +export class MessageFromAgentIdDTO { + @IsNotEmpty() + @IsString() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + thread_id: string; + + @IsOptional() + @IsInt() + @Min(1) + @Max(100) + limit_message?: number; +} + +/** + * Interface for message requests to agents + */ +export class MessageRequest { + @IsNotEmpty() + @IsString() + @IsUUID() + agent_id: string; + + @IsString() + @Length(1, 10000) + content: string; + + @IsOptional() + @IsInt() + @Min(0) + @Max(1) + hitl_threshold?: number; +} + +export class Message { + @IsString() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + @Length(1, 10000) + content: string; +} + +export class AgentRequestDTO { + @IsNotEmpty() + request: Message; +} + +export class SupervisorRequest { + @IsNotEmpty() + @IsString() + @Length(1, 10000) + content: string; + + @IsOptional() + @IsString() + @IsUUID() + agentId?: string; // Optional: specify which agent to use +} + +export class SupervisorRequestDTO { + @IsNotEmpty() + request: SupervisorRequest; +} + +export class getMessagesFromAgentsDTO { + @IsNotEmpty() + @IsString() + @IsUUID() + agent_id: string; + + @IsNotEmpty() + @IsString() + thread_id: string; +} diff --git a/packages/core/src/common/server/dto/agents.ts b/packages/core/src/common/server/dto/agents.ts deleted file mode 100644 index 5d3e2eeac..000000000 --- a/packages/core/src/common/server/dto/agents.ts +++ /dev/null @@ -1,358 +0,0 @@ -import { isNull } from 'util'; -import { AgentConfig } from '../../agent.js'; -import { - IsNotEmpty, - IsOptional, - IsString, - IsUUID, - IsArray, - Length, - Min, - Max, - IsInt, - Matches, - ArrayNotEmpty, - ArrayMinSize, - ArrayMaxSize, - isNotEmpty, - isUUID, - IsIn, -} from 'class-validator'; - -/** - * Configuration for agent memory settings - */ -export interface AgentMemory { - enabled: boolean; - short_term_memory_size: number; - memory_size: number; -} - -/** - * Configuration for agent rag settings - */ -export interface AgentRag { - enabled: boolean; - embedding_model: string | null; -} - -/** - * DTO for adding a new agent - */ -export class AddAgentRequestDTO { - @IsNotEmpty() - agent: AgentConfig.Input; -} - -/** - * DTO for retrieving messages from a specific agent - */ -export class MessageFromAgentIdDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - thread_id: string; - - @IsOptional() - @IsInt() - @Min(1) - @Max(100) - limit_message?: number; -} - -/** - * Interface for message requests to agents - */ -export class MessageRequest { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsString() - @Length(1, 10000) - request: string; - - @IsOptional() - @IsInt() - @Min(0) - @Max(1) - hitl_threshold?: number; -} - -/** - * DTO for deleting multiple agents - */ -export class AgentsDeleteRequestDTO { - @IsArray() - @ArrayNotEmpty() - @ArrayMinSize(1) - @ArrayMaxSize(100) - @IsUUID(undefined, { each: true }) - agent_id: string[]; -} - -/** - * DTO for updating model configuration - */ -export class UpdateModelConfigDTO { - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^[a-zA-Z0-9_-]+$/, { - message: - 'Provider must contain only alphanumeric characters, hyphens, and underscores', - }) - provider: string; - - @IsNotEmpty() - @IsString() - @Length(1, 100) - @Matches(/^[a-zA-Z0-9._:-]+$/, { - message: - 'Model name must contain only alphanumeric characters, dots, colons, hyphens, and underscores', - }) - modelName: string; - - @Min(0) - @Max(2) - @Matches(/^-?\d+(\.\d+)?$/, { message: 'Temperature must be a number' }) - temperature: number; - - @IsInt() - @Min(1) - maxTokens: number; -} - -export class Message { - @IsOptional() - @IsUUID() - agent_id?: string; - - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^[a-zA-Z_]+$/, { - message: 'Sender type must contain only letters and underscores', - }) - sender_type: string; - - @IsOptional() - @IsNotEmpty() - @IsString() - @Length(1, 10000) - content?: string; - - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^[a-zA-Z_]+$/, { - message: 'Status must contain only letters and underscores', - }) - status: string; -} - -export class AgentRequestDTO { - @IsNotEmpty() - request: Message; -} - -export class SupervisorRequest { - @IsNotEmpty() - @IsString() - @Length(1, 10000) - content: string; - - @IsOptional() - @IsUUID() - agentId?: string; // Optional: specify which agent to use -} - -export class SupervisorRequestDTO { - @IsNotEmpty() - request: SupervisorRequest; -} - -export class getMessagesFromAgentsDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - thread_id: string; -} -export class InitializesRequestDTO { - @IsArray() - @ArrayNotEmpty() - @ArrayMinSize(1) - @ArrayMaxSize(50) - agents: AgentConfig.Input[]; -} - -export class AgentDeleteRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; -} - -export class AgentDeletesRequestDTO { - @IsArray() - @ArrayNotEmpty() - @ArrayMinSize(1) - @ArrayMaxSize(100) - @IsUUID(undefined, { each: true }) - agent_id: string[]; -} - -export class AgentAddRequestDTO { - @IsNotEmpty() - agent: AgentConfig.Input; -} - -export class AgentAvatarResponseDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - @Length(1, 50) - @Matches(/^image\/(jpeg|png|gif|webp)$/, { - message: 'MIME type must be a valid image format', - }) - avatar_mime_type: string; -} - -export type AgentResponse = - | { status: 'success'; data: T } - | { status: 'waiting_for_human_input'; data?: T } - | { status: 'failure'; error: string; data?: T }; - -/** - * Request to get a specific agent’s MCP config - */ -export class GetAgentMcpsRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; -} - -/** - * Request to get all MCP server of a specific agent - */ -export class AgentMCPRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - mcp_id: string; -} - -/** - * Request to update the value of one secret in a given MCP server - */ -export class UpdateMcpEnvValueRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - mcp_id: string; - - @IsNotEmpty() - @IsString() - secret_name: string; - - @IsNotEmpty() - @IsString() - secret_value: string; -} - -/** - * Request to rename a secret key in an MCP config - */ -export class UpdateMcpEnvNameRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - mcp_id: string; - - @IsNotEmpty() - @IsString() - old_name: string; - - @IsNotEmpty() - @IsString() - new_name: string; -} - -/** - * Request to replace --key value or --profile with a new value - */ -export class UpdateMcpValueRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - @IsString() - mcp_id: string; - - @IsNotEmpty() - @IsString() - new_value: string; -} - -/** - * Request to delete multiple MCP servers - */ -export class DeleteMultipleMcpServersRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsArray() - @ArrayNotEmpty() - mcp_ids: string[]; -} - -/** - * Request to add one or more new MCP servers - */ -export class AddMcpServerRequestDTO { - @IsNotEmpty() - @IsUUID() - agent_id: string; - - @IsNotEmpty() - mcpServers: Record< - string, - { - command: string; - args?: string[] | string; - env?: Record; - [key: string]: any; - } - >; -} - -/** - * Request to update an entire agent’s MCP server object - */ -export class UpdateAgentMcpDTO { - @IsNotEmpty() - @IsUUID() - id: string; - - @IsNotEmpty() - mcp_servers: Record; // Replace Record -} diff --git a/packages/core/src/common/server/dto/index.ts b/packages/core/src/common/server/dto/index.ts new file mode 100644 index 000000000..ae24721c0 --- /dev/null +++ b/packages/core/src/common/server/dto/index.ts @@ -0,0 +1,4 @@ +// Re-export all DTOs from organized structure +export * from './agent/config.dto.js'; +export * from './agent/message.dto.js'; +export * from './agent/mcp.dto.js'; diff --git a/packages/core/src/common/server/dto/websocket.ts b/packages/core/src/common/server/dto/websocket.ts deleted file mode 100644 index 7ad90dd77..000000000 --- a/packages/core/src/common/server/dto/websocket.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { IsNotEmpty } from 'class-validator'; -import { MessageRequest } from './agents.dto.js'; -import { AgentConfig } from '@common/agent.js'; -export class WebsocketAgentAddRequestDTO { - @IsNotEmpty() - agent: AgentConfig.Input; - @IsNotEmpty() - socket_id: string; -} - -export class WebsocketAgentRequestDTO { - @IsNotEmpty() - request: MessageRequest; - @IsNotEmpty() - socket_id: string; -} - -export class WebsocketAgentDeleteRequestDTO { - @IsNotEmpty() - agent_id: string; - @IsNotEmpty() - socket_id: string; -} - -export class WebsocketGetAgentsConfigRequestDTO { - @IsNotEmpty() - socket_id: string; -} - -export class WebsocketSupervisorRequestDTO { - @IsNotEmpty() - request: { - content: string; - agentId?: string; - }; - @IsNotEmpty() - socket_id: string; -} - -export class WebsocketGetMessagesRequestDTO { - @IsNotEmpty() - agent_id: string; - thread_id: string; - @IsNotEmpty() - socket_id: string; - limit_message: number | undefined; -} diff --git a/packages/core/src/common/server/interfaces/agents.ts b/packages/core/src/common/server/interfaces/agents.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/core/src/config/database.config.ts b/packages/core/src/config/database.config.ts index 5d9210cf3..bd7f0342e 100644 --- a/packages/core/src/config/database.config.ts +++ b/packages/core/src/config/database.config.ts @@ -1,4 +1,4 @@ -import { DatabaseCredentials } from '../common/agent.js'; +import { DatabaseCredentials } from '../common/agent/interfaces/agent.interface.js'; /** * Global database configuration service diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0c71646d7..cc140afef 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,6 @@ -export type * from './common/agent.js'; +export type * from './common/agent/interfaces/agent.interface.js'; -export * from './common/agent.js'; +export * from './common/agent/interfaces/agent.interface.js'; export { default as logger } from './logger/logger.js'; @@ -12,8 +12,7 @@ export * from './common/constant/default-agent.constant.js'; export * from './common/constant/agents.constants.js'; export type { RagConfigSize } from './types/rag/ragConfig.js'; export type { GuardsConfig } from './config/guards/guardsSchema.js'; -export * from './common/server/dto/agents.js'; -export * from './common/server/dto/websocket.js'; +export * from './common/server/dto/index.js'; export { CustomHuggingFaceEmbeddings, type CustomHuggingFaceEmbeddingsParams, diff --git a/packages/core/src/services/agent-validation.service.ts b/packages/core/src/services/agent-validation.service.ts index 572df9dd9..553f40e4b 100644 --- a/packages/core/src/services/agent-validation.service.ts +++ b/packages/core/src/services/agent-validation.service.ts @@ -1,5 +1,5 @@ import { getGuardValue } from './guards.service.js'; -import { AgentConfig } from '../common/agent.js'; +import { AgentConfig } from '../common/agent/interfaces/agent.interface.js'; import logger from '../logger/logger.js'; /** diff --git a/packages/server/mcp.txt b/packages/server/mcp.txt deleted file mode 100644 index a4c10a9b4..000000000 --- a/packages/server/mcp.txt +++ /dev/null @@ -1,534 +0,0 @@ -# Car Import Trends from Germany and Spain to Portugal - -## 1. Import Statistics and Trends -### Market Overview -- In 2023, Portugal imported **$7.13B** worth of cars, with Spain as the top origin, accounting for **$2.09B**. -- Germany was the second-largest source of imports, contributing **$1.55B**. - -### Pricing Dynamics -- The automotive market has shown a fluctuation in car prices influenced heavily by demand and supply dynamics across Germany and Spain. - -### Market Share Analysis -- Spain holds a significant share in Portugal's car market with a growth rate of **+759M** from 2022 to 2023. Germany follows as a major player in the import market. - -## 2. Trade Regulations and Tariffs -### Overview -- The import policies governing vehicles from Germany and Spain have seen various updates, affecting cost structures and compliance obligations. -- The EU's regulatory measures on CO2 emissions impose additional compliance costs for imported vehicles. - -## 3. Consumer Preferences -### Trends -- Portuguese consumers are increasingly opting for electric and hybrid vehicles, aligning with global automotive trends. This shift is expected to influence future import dynamics. -# Regulatory Insights and Trade Regulations with Effect on Car Imports - -## Chronological Records of Regulatory Changes -### Recent Changes -- The European Union has recently introduced stricter emission regulations that mandate all new vehicles to meet enhanced standards by 2025, affecting imports from all member states, including Germany and Spain. -- Tariff alterations have occurred based on trade agreements negotiated at bilateral and EU levels, impacting the cost of cars from Germany and Spain. -# Daily Sales Log Template - -## Date: ____________ - ---- - -### Customer Interaction -- **Customer Name:** ____________ -- **Contact Information:** ____________ - -### Sales Details -1. **Guitars Sold:** - - Model: ____________ - - Serial Number: ____________ - - Sale Price: ____________ - - Payment Method: ____________ - -2. **Special Orders:** - - Item: ____________ - - Estimated Arrival: ____________ - - Notes: ____________ - ---- - -### Additional Notes -- ________________________________________________________ -- ________________________________________________________ -- ________________________________________________________ - ---- - -### Signature of Sales Representative: ____________ -# Customer Contact Database - -## Customer Details -| Name | Contact Information | Follow-Up Dates | Preferences | -|------|--------------------|------------------|-------------| -| | | | | -# Guitar Inventory and Sales Performance Log - -## Inventory Status -| Guitar Model | Quantity in Stock | Sales Speed | Restocking Needs | -|----------------|-------------------|-------------|------------------| -| | | | | - -## Sales Performance -| Date | Guitar Model | Units Sold | Notes | -|----------------|----------------|-------------|------------------| -| | | | | -# Customer Feedback and Guitar Reviews - -## Overview -This document compiles customer feedback and product reviews on different guitar models sold to aid potential buyers in making informed choices and enhancing their shopping experience. - -## Feedback Categories -- **Model Name** -- **Customer Name** -- **Date of Purchase** -- **Review Rating (1-5)** -- **Review Comments** - -## Example Reviews -### Fender Stratocaster -- **Customer Name:** John Doe -- **Date of Purchase:** 2022-06-10 -- **Review Rating:** 5 -- **Review Comments:** "Absolutely love this guitar! The sound is incredible and it feels great to play." - -### Gibson Les Paul -- **Customer Name:** Jane Smith -- **Date of Purchase:** 2022-08-15 -- **Review Rating:** 4 -- **Review Comments:** "Very solid guitar, but it's a bit heavy for my liking. The tone is rich and warm." - -### Yamaha FG800 -- **Customer Name:** David Brown -- **Date of Purchase:** 2022-09-20 -- **Review Rating:** 5 -- **Review Comments:** "For the price, you can't beat this acoustic. Great sound and playability!" -Customer Interaction Details: - -Name: Etienne - -Preferences: -- Acoustic Guitars -- Electric Guitars - -Guitars Considered: -1. C.F. Martin D-28 (~$3,000) -2. Larrivee OM-03 (~$1,800) -3. Fender American Professional II Stratocaster (~$1,400) -4. Gibson Les Paul Standard 50s (~$2,500) -5. Yamaha FG830 (~$500) -6. Squier Classic Vibe '70s Stratocaster (~$450) - -Purchase Decisions: -- Selected the C.F. Martin D-28 for its premium quality and sound. -### Market Analysis Report on Car Importation from Germany and Spain to Portugal - -#### 1. Overview -In 2023, Portugal experienced a significant flow of car imports from Germany and Spain, amounting to $7.13 billion overall, with a notable trend of Spain becoming the fastest growing import source, increasing its contribution by $759 million from 2022 to 2023. Total car exports from these countries to Portugal reached $4.95 billion. - -#### 2. Import Statistics -- **Total Car Imports from Germany and Spain**: $7.13 billion -- **Car Exports to Portugal**: $4.95 billion -- **Fastest Growing Source**: Spain (+$759 million) -- **Key Import Origins**: - - Spain: $2.09 billion - - Germany: $1.55 billion - - France: $1.11 billion - -#### 3. Regulatory Findings -- A 25% U.S. tariff on imported cars has been effective since April 3, 2025. This regulatory change is expected to influence pricing strategies in the automotive industry. -- The EU has introduced potential tariff reductions on car exports to the U.S. down to 15%, contingent on agreements concerning tariffs on U.S. goods. -- Compliance regulations include: - - National homologation within 20 days. - - Vehicle inspection and registration processes within specified timelines. - -#### 4. Quality Concerns -Recent data indicate that 3.4% of cars imported from Germany have tampered odometers, and 22.8% show signs of damage. These figures highlight the need for thorough inspections of used vehicles. - -#### 5. Conclusions -The shifting landscape of automotive tariffs and regulations is reshaping the import-export dynamics in Europe, significantly affecting Portugal's car importation strategy in dealings with Germany and Spain. Continuous monitoring of market trends and regulatory landscapes will be essential for stakeholders in the automotive sector. -### Executive Summary -The analysis of car importation from Germany and Spain to Portugal reveals significant trends in demand, pricing strategies, and model availability. Key insights indicate a strong preference for electric and hybrid vehicles, competitive pricing dynamics influenced by currency fluctuations, and the need for adaptability by importers to changing consumer preferences. - -### Key Findings - -1. **Market Demand Trends**: - - Total car imports to Portugal from Germany and Spain increased by **15%** YoY in 2024, reflecting robust consumer purchasing confidence. - - Electric and hybrid vehicle sales have surged, making up **40%** of total imports from Germany, compared to **25%** from Spain. - - The increasing priority on sustainability among Portuguese consumers has doubled the demand for EVs in the last two years. - -2. **Pricing Strategies**: - - Competitive pricing strategies have emerged, with German models offering average prices of **€30,000**, while Spanish imports average **€25,000**. - - Currency fluctuations, particularly the depreciation of the Euro against the Dollar, have influenced the pricing strategies; importers are adjusting margins to maintain competitiveness. - - Innovations in financing options, including flexible lease-to-own plans, have become essential in attracting buyers. - -3. **Model Availability**: - - Popular models from Germany include the **Volkswagen ID.3**, with **7,500 units** imported in 2024. From Spain, the **Seat Leon** has seen **5,000 units** imported. - - New entrants in the electric segment, such as the **Škoda Enyaq**, have gained traction, with **3,200 units** recorded in the first half of 2024. - -4. **Consumer Preferences**: - - Portuguese consumers increasingly prioritize fuel efficiency and eco-friendliness, opting for cars with CO2 emissions below **100 g/km**. - - The need for digital features, such as advanced driver-assistance systems (ADAS) and in-car connectivity, is growing among younger buyers. - -### Data Visualization -A potential visualization approach could include: -- A **bar chart** comparing the number of units imported by car model from each country. -- A **line graph** depicting YoY growth in electric vehicle imports over the past five years. - -### Conclusions and Recommendations - -1. **Target Electric Vehicle Importation**: - - Focus on sourcing electric and hybrid vehicles from Germany to meet the rising demand in Portugal, potentially negotiating better deals with manufacturers. - -2. **Dynamic Pricing Strategies**: - - Implement flexible pricing strategies that account for currency fluctuations and adjust margins accordingly to remain competitive. - -3. **Leverage Financing Options**: - - Provide innovative financing options, such as subscriptions or lease-to-own schemes, to appeal to younger demographics who prioritize convenience and flexibility. - -4. **Adapt to Consumer Preferences**: - - Regularly update the inventory in line with emerging consumer trends, notably eco-friendliness and digital enhancements, reinforcing brand perceptions that cater to modern buyer needs. - -By adopting these recommendations, car importers can position themselves effectively in the dynamic Portuguese market, maximizing profitability and market share as demand evolves. -# Car Import Trends Portugal - -## Section 1: Data Analysis -Analyzed car import volumes and prices from customs databases in Germany and Spain, including information on tariffs and regulations. - -### Key Findings: -- Import volumes from Germany: [Data to be inserted] -- Import volumes from Spain: [Data to be inserted] -- Price trends in imports: [Data to be inserted] - -## Section 2: Market Demand Metrics -Gathered from industry reports and surveys revealing consumer preferences and automotive trends in Portugal. - -### Key Insights: -- Consumer preferences: [Data to be inserted] -- Market demand trends: [Data to be inserted] -- Regulatory changes: [Data to be inserted] -# Car Import Trends Portugal - -## Executive Summary -This document outlines the car importation trends from Germany and Spain to Portugal, analyzing import volumes, pricing data, and market demand metrics. Key findings will be documented to assist stakeholders in understanding market dynamics and consumer preferences regarding imported vehicles. - -## 1. Importation Trends from Germany and Spain -- **Volume of Imports**: - - Germany: In 2023, Portugal imported approximately 45,000 vehicles from Germany, a 10% increase from 2022. - - Spain: The number of vehicles imported from Spain in 2023 was about 35,000, reflecting stable year-on-year demand. - -- **Tariffs and Regulations**: - - Both countries have seen minimal tariff changes in recent years. Portugal applies a standard European Union tariff rate of 10% on cars. - - Environmental regulations play a significant role in import selections, with a preference for low-emission vehicles. - -## 2. Pricing Dynamics -- **Average Prices**: - - The average price of vehicles imported from Germany was around €28,000, while imports from Spain averaged €22,000 for 2023. - - Pricing has been influenced by demand for models with higher fuel efficiency and technology features, with electric vehicles gaining popularity. - -## 3. Market Demand Metrics in Portugal -- **Consumer Preferences**: - - Surveys reveal a 30% preference for hybrid and electric vehicles over traditional combustion engine vehicles in 2023. - - Demand for vehicles under €25,000 remains strong due to budget-conscious consumers. - -- **Future Trends**: - - Market forecasts suggest continued growth in electric vehicle imports, projected to increase by 20% by 2024 as new models become available. - -## Conclusion and Recommendations -- Focusing on electric and hybrid models can strategically align import activities with consumer preferences. -- Collaborating with automobile manufacturers to offer competitive pricing on electric vehicles could enhance market positioning. - ---- -Date of Report: [Insert Date] -# Car Import Trends Portugal - -## Executive Summary -This document captures key findings regarding car importation trends from Germany and Spain to Portugal, including volume statistics, pricing dynamics, and market demand insights. - -## 1. Import Data Analysis -### 1.1. Import Volumes -- **Germany:** Over the past year, car imports from Germany to Portugal have increased by 15%, amounting to approximately 45,000 units in 2024. -- **Spain:** The import figure from Spain has also shown a robust growth of 12%, with about 35,000 cars imported in the same period. - -### 1.2. Pricing Dynamics -- **Average Price per Unit**: - - Germany: €35,000 - - Spain: €28,000 -- Tariffs and Regulations: Both sources indicate a tariff of 10% on imported vehicles, requiring compliance with EU automotive regulations. - -## 2. Market Demand Metrics -### 2.1. Consumer Preferences -Recent surveys show: -- 62% of Portuguese consumers prioritize fuel efficiency and emissions ratings when purchasing imported cars. -- Popular models include compact SUVs, with German brands leading in preference, followed closely by Spanish brands, particularly in the mid-range segment. - -### 2.2. Emerging Trends -- The trend towards electric vehicles is accelerating, with requests for electric imports increasing by 25% YoY. -- Payment and financing options heavily influence purchasing decisions, with 40% of consumers considering available financing plans. - -## Conclusion and Recommendations -- Focus marketing efforts on fuel-efficient and electric models to align with consumer trends. -- Develop partnerships with local financing institutions to enhance accessibility for potential buyers. -- Regularly monitor legislative changes that may affect tariffs and vehicle compliance standards. -### Key Findings on Car Import Trends in Portugal - -1. **Import Volumes and Trends**: - - In recent years, Spain has been the largest supplier of motor vehicles to Portugal, contributing significantly higher import values compared to Germany. - - Data from 2022 shows that used light passenger vehicle imports in Portugal increased year-over-year, reversing the previous decline during the pandemic (2020). - -2. **Market Dynamics for Electric Vehicles (EVs)**: - - Electric vehicle registrations in Portugal have seen substantial growth, with a 23% year-over-year increase in EV registrations noted across Europe in mid-2025. - - The Tesla Model Y led the market as the best-selling EV, indicating a shift towards eco-friendly vehicles amidst regulatory support for decreasing emissions. - -3. **Competitive Factors**: - - Regulatory reforms in Portugal are facilitating the establishment of charging infrastructure, such as Tesla's plans to add 100 Supercharger stations. This enhances the attractiveness of EVs for potential buyers. - - With enhanced competition from both German and Spanish markets, pricing structures are critical. Estimated costs for importing cars from Germany to Portugal can range from €2,800 to €5,000, excluding the vehicle price, impacting overall affordability for consumers. - -4. **Brand Performance**: - - Peugeot emerged as the best-selling car brand in Portugal in 2023, outperforming competitors like Renault, highlighting consumer preferences towards specific brands and presumably higher value propositions in their offerings. - -5. **Market Outlook**: - - Growth trends suggest a favorable environment for both internal combustion engine and electric vehicles, especially as consumer acceptance of EVs increases and infrastructure improves. - -### Conclusive Insights -- **Demand for Used and New Vehicles**: The segment for used light vehicles continues to gain traction next to new vehicle imports, particularly due to economic recovery signals post-pandemic. -- **Implications for Suppliers**: Suppliers from Germany should leverage their brand reputation but compete with strategic pricing and local adaptations to resonate with Portuguese consumers alongside the seasoned Spanish importers. -- **EV Market Potential**: The growth in electric vehicles, supported by government initiatives, presents significant opportunities for manufacturers and importers looking to enter or expand in this burgeoning market segment. - -### Recommendations for Car Importers -1. **Target Marketing Strategies**: Develop marketing approaches focusing on the rising EV segment, particularly for brands like Tesla and local brands that focus on affordability and sustainability. -2. **Cost Management**: Evaluate and optimize the total cost of importing vehicles, ensuring transparent pricing structures, particularly for imported used vehicles to attract cost-conscious buyers. -3. **Infrastructure Partnerships**: Collaborate with local providers to enhance charging infrastructure, critically in metropolitan areas to boost EV sales and consumer confidence. -4. **Data-Driven Insights**: Continuously monitor import and sales statistics to identify emerging trends, allowing for agile adaptations of inventory and marketing strategies to consumer demands. -# Key Findings on Car Importation Trends from Germany and Spain to Portugal - -## 1. Market Overview -- **Import Value and Sources**: In 2024, Portugal's car imports from Germany amounted to $5.83 billion, while Spain's imports were significant, with total vehicles imported from various nations marking it as the 10th largest car importer globally. -- **Key Players**: Germany and Spain are critical suppliers, with Germany alone contributing major value to Portugal's imports. Spain exported $43.2 billion in cars in 2023, ranking it as the 7th largest global car exporter, and it imported $23.5 billion worth of vehicles. - -## 2. Pricing Dynamics -- **Tariffs and Costs**: Import duties on vehicles in Spain are set at 10% with an additional VAT of 21%, combined with registration fees dependent on the vehicle's emissions. These costs can significantly impact pricing dynamics in the Portuguese market as they determine the final retail prices of imported vehicles. - - **Example Calculation**: An SUV valued at $40,000 could incur approximately $14,000 in additional costs (tariffs + VAT + registration), resulting in a final cost that may deter many potential buyers. - -## 3. Competitive Landscape -- **Demand for Electric Vehicles**: The EU has seen a dramatic increase in battery-only electric vehicle registrations, rising by 32% in 2024. Portugal is likely to mirror this trend, with growing consumer awareness and government incentives for green technology. This shift presents opportunities for importers focusing on expanding electric vehicle offerings. - -## 4. Trends and Consumer Preferences -- **Fleet Age and Usage**: The average age of registered vehicles in Portugal trends towards older models, which points to a demand for newer, more efficient vehicles, particularly as environmental regulations tighten. Car importers should target marketing efforts towards newer models, especially electric and hybrid vehicles that comply with these regulations. - -- **Underrepresented Segments**: Despite the rise in popular electric vehicles, overall alternative fuel vehicles still maintain a niche status. Therefore, there's an opportunity for market players to diversify their portfolios to include a range of hybrids and less conventional vehicles. - -### Insights and Implications for the Automotive Market -1. **Strategic Focus on Electrification**: Companies should prioritize stocking electric and hybrid vehicles to meet consumer demand, especially as environmental regulations gain traction. Establishing partnerships with electric vehicle manufacturers may also yield beneficial outcomes. -2. **Pricing Strategy**: Understanding the total cost of ownership (TCO) for vehicles, including all taxes and import fees, is critical. Retailers should communicate transparency around these costs to assist consumers in making informed decisions, potentially promoting competitive financing options. -3. **Market Education & Awareness**: Developing educational programs directed toward consumers about the benefits of electric and newer vehicles can help alleviate concerns on price and emissions, making imported vehicles more attractive. -4. **Collaboration with local dealerships**: Importers would benefit by collaborating with local Portuguese dealerships to improve distribution logistics and customer engagement, enhancing the overall buying experience. - -### Conclusion -The data indicates a robust landscape for car imports from Germany and Spain to Portugal, driven heavily by trends in electrification and pricing awareness. With the automotive industry facing continual change, stakeholders must adapt their strategies to leverage emerging trends such as sustainability and customer education. By aligning offerings with these opportunities, businesses can enhance their competitive edge in the vibrant Portuguese automotive market. -### Comprehensive Market Analysis Insights for Automotive Trade Stakeholders - -#### Executive Summary: -The automotive import market in Portugal has shown notable dynamism amid regulatory changes, economic fluctuations, and shifting consumer preferences. As of 2023, Portugal imported approximately **$7.13 billion** worth of cars, driven primarily by imports from Spain and Germany. This overview documents critical insights and recommendations derived from a comprehensive analysis of market trends, pricing dynamics, and regulatory changes. - ---- - -### Key Findings - -#### 1. Import Dynamics -- **Major Sources of Imports:** Spain and Germany are primary contributors, collectively accounting for **over $3.64 billion** in 2023. -- **Growth Trends:** Import from Spain grew by **$759 million** between 2022 and 2023. - -#### 2. Pricing Variations -- **Import Tariffs:** An average import tariff of **3%** and a VAT of **23%** for non-EU imports significantly influence end pricing. -- **Future Tariff Changes:** New tariffs effective on **August 7, 2025**, may complicate pricing strategies. - -#### 3. Demand Fluctuations -- **Consumer Preferences:** A strong demand for budget vehicles priced under **$25,000**. - -#### 4. Regulatory Impacts -- **Compliance Costs:** Importers must navigate complex regulations and ensure compliance to minimize costs and delays. - -#### 5. Competitive Landscape -- **Key Competitors:** Volkswagen (VW) and Renault dominate due to established supply chains. Watch for Spanish brands like SEAT and Ford Spain, gaining traction. - -### Recommendations -1. Implement dynamic pricing strategies to offset tariff impacts. -2. Monitor trade policies and regulatory updates closely. -3. Expand budget vehicle offerings and maintain cost efficiencies. -4. Develop local partnerships to enhance market responsiveness. -5. Engage consumers through awareness campaigns for newer models. -### Updated Market Insights on Car Import Dynamics - -#### Key Updates: -- **Import Trends:** The overall import of cars into Portugal stands at **$7.13 billion** for 2023, with notable contributions from: - - **Spain:** $2.09 billion - - **Germany:** $1.55 billion -- **Tariff Changes:** New tariffs effective from **August 7, 2025**, may affect pricing strategies further. - -#### Additional Notes: -- **Consumer Sensitivity:** Heightened price sensitivity observed in the post-pandemic economic landscape. -- **Future Considerations:** Ongoing shifts in U.S. trade policies can impact price competitiveness of U.S. manufactured cars in Portugal, requiring proactive strategies. - -### Daily Log for Automotive Import Market Trends - -#### Date: [Insert Today's Date] -- **Event/Insight**: [Describe significant market changes, regulations updates, or competitive moves] -- **Source**: [Mention source of information] - -#### Date: [Next Date] -- **Event/Insight**: [Describe significant market changes, regulations updates, or competitive moves] -- **Source**: [Mention source of information] - -*Continue this log daily to ensure real-time tracking and documentation of the market trends affecting automotive imports in Portugal. -### Real-Time Monitoring and Documentation of Emerging Automotive Market Trends in Portugal - -**Objective**: -Establish a framework for continuous tracking of automobile import market trends in Portugal; document emerging insights, consumer preferences, and competitive shifts in real-time. - ---- - -### T0: Google Doc Creation -- **Action**: A dedicated Google Doc for real-time tracking of market trends in Portuguese automotive imports is created. -- **Purpose**: To enable cascading updates from various sources and maintain up-to-date information readily available for decision-making. - -### T1: Daily Log Implementation -- **Mechanism**: Implement a daily log mechanism in Google Docs to capture significant market changes, regulations updates, and competitive moves affecting the automotive import landscape. -- **Structure**: A chronological log maintained in Google Docs that documents key events and trends affecting automotive imports in Portugal. -# Documentation of Car Import Trends and Findings - -## Executive Summary -This report provides a comprehensive analysis of car importation patterns in Portugal, focusing on imports from Germany and Spain, with insights on demand, pricing, and industry regulations. - -### Key Findings: -1. **Car Imports Overview** - - Total imports from Germany: **$2.68 billion** (2024) - - Total imports from Spain: **$2.09 billion** (2024) - -2. **Historical Growth Rates:** - - From Germany (2018-2022): **7.5% CAGR** - - From Spain (2018-2022): **6.3% CAGR** - -3. **Average Prices of Imported Vehicles (2024)**: - - From Germany: - - Compact cars: **$24,000** - - SUVs: **$30,000** - - Electric vehicles: **$39,000** - - From Spain: - - Compact cars: **$20,500** - - SUVs: **$25,500** - - Electric vehicles: **$35,500** - -4. **Consumer Preferences**: More than 65% of consumers prefer compact cars, with a growing interest in electric vehicles which accounted for **20%** of all purchases. - -5. **Projected Demand Growth**: Demand for imported cars is expected to increase by **8%** in the upcoming year due to positive economic indicators and consumer trends. -## Trade Insights - -### Trade Dynamics between Portugal and Spain -- Spain exported vehicles valued at **$2.09 billion** to Portugal in 2023, highlighting the importance of the automotive sector in bilateral trade. - -#### Trade Statistics for May 2025 -- Spain to Portugal: - - Total Exports: **€2.84 billion** - - Key Products: - - Cars: **€111 million** - -- Portugal to Spain: - - Total Exports: **€1.35 billion** - - Key Products: - - Motor Vehicles; parts & accessories: **€85 million** - -### Trade Insights between Portugal and Germany -- Germany remains a crucial partner in automobile imports for Portugal, demonstrating a strong market for vehicles and components. The analysis shows that Germany's car exports to Portugal reached **$1.37 billion**. - -### Proposed Pricing Strategies -- Implement tiered pricing strategies to target various consumer segments. -- Promote electric vehicles through market-specific campaigns to align with current trends. - -## Recommendations -1. **Adjust Pricing Strategies**: Align prices based on consumer perceptions of vehicle value. -2. **Focus on Electric Vehicle Imports**: Meeting the increasing consumer demand for sustainable transport options. -3. **Engage in Consumer Research**: Ongoing analysis of shifting preferences to inform marketing and import strategies. -# Market Analysis and Recommendations: Automotive Imports from Germany and Spain to Portugal - -## Executive Summary -The automotive import market in Portugal displays significant reliance on imports from Germany and Spain, which together have established a competitive landscape characterized by specific product segments and emerging regulatory frameworks. Notable trends include the increase in imported vehicle value, shifts towards high-quality automotive standards, and growing concerns over used vehicle integrity. This synthesis aims to leverage these insights into actionable recommendations for stakeholders focusing on market opportunities and pricing strategies. - -### Key Findings -1. **Market Value and Penetration**: - - In 2024, Portugal imported **$12.78 billion worth** of goods from Germany, with vehicles comprising a substantial portion valued at **$2.68 billion**. In contrast, car imports from Spain totaled **$2.09 billion** in the same year. - - Monthly import trends show an increase from **€1,036 million** in January 2025 to **€1,067 million** in February 2025 for German imports, indicating a consistent demand trajectory. - -2. **Consumer Preferences**: - - A large segment of the vehicle imports consists of used cars, catalyzing concern for quality, particularly as **3.4%** of German vehicles have tampered odometers. The implications for buyers and dealers are substantial, as consumers lean towards trusted brands and verified histories. - -3. **Regulatory Changes**: - - The overhaul of the **Imposto Único de Circulação (IUC)** tax starting in 2026 simplifies tax payments but emphasizes the need for compliance and timely registration of imported vehicles. Notably, electric vehicles are exempt from this tax, presenting an opportunity for promoting eco-friendly models. - -4. **EU Policies**: - - The EU is contemplating the introduction of minimum price strategies for imports, particularly from China, as well as regulations enhancing safety standards like **Regulation 2019/2144** which enforces stringent testing for imported vehicles, directly impacting compliance costs for importers. - -### Market Opportunities -1. **Expand Electric Vehicle Imports**: - - With the exemption from the IUC tax, there is a favorable environment for importing electric vehicles (EVs). Developing partnerships with manufacturers of EVs, especially German brands known for innovation, can capture the growing consumer shift towards sustainability. - -2. **Enhance Used Vehicle Quality Assurances**: - - Establishing certifications and guarantees on the condition of used vehicles can differentiate importers. Offering warranties or service plans could mitigate consumer concerns regarding vehicle integrity and foster trust. - -3. **Leverage Competitive Pricing Strategies**: - - Given the substantial volume of imports from Spain and Germany, competitive pricing in vehicles can attract consumer interest. Importers must analyze local market conditions to establish pricing models that balance demand with quality assurance. - -4. **Engage in Compliance Education**: - - Educating potential importers and buyers about new regulatory changes and tax structures will build confidence in compliance and trading procedures. Implementing a transparent communication strategy regarding import taxes will simplify the process and enhance importer relations. - -### Recommendations -- **Market Positioning**: Focus on positioning brands that capture high demand segments. German imports, known for quality, should be marketed emphasizing reliability and performance. -- **Partnerships**: Form alliances with automotive manufacturers in Germany and Spain to align supply chains that cater to growing consumer preferences while ensuring timely deliveries. -- **Consumer Education Programs**: Initiate educational campaigns on the benefits of purchasing certified used vehicles and the factors influencing vehicle quality. Highlighting tax advantages for electric vehicles will help position yourself as a thought leader in the market. -- **Regular Market Reviews**: Establish a routine analysis of import trends and consumer behavior shifts to adapt quickly to changes in the automotive landscape. Staying informed allows stakeholders to capitalize on emerging opportunities effectively. - -By implementing these strategies, stakeholders can optimize their positioning within the automotive import market and cater effectively to consumer expectations while navigating regulatory changes. -# Strategic Recommendations Based on Automotive Market Analysis - -## 1. Enhance Electric Vehicle Infrastructure -- Invest in charging stations across urban and rural areas to alleviate range anxiety and promote EV adoption. - -## 2. Market Campaigns -- Develop campaigns targeting younger demographics to emphasize the benefits of owning electric vehicles, including savings, environmental impact, and available incentives. - -## 3. Government Collaboration -- Work with governmental bodies to advocate for better financing options and tax incentives. - -## 4. Ongoing Market Research -- Conduct continuous assessments of consumer preferences to adapt marketing strategies accordingly. -# Summary of Car Import Trends and Findings from Germany and Spain to Portugal - -## 1. Overview of Trade Dynamics -The relationship between Portugal and Germany, specifically in terms of car imports, showcases significant financial activity with total imports valued at **$12.78 billion** from Germany in 2024. - -### 1.1 General Trade Data -- Overall imports from Germany: **$12.78 billion** (2024) -- Vehicles comprise a substantial portion: **$2.68 billion** (2024). - -### 1.2 Key Import Sources for Cars -- Total car imports: **$7.13 billion** (2023). -- From Germany: **$1.55 billion**. -- From Spain: **$2.09 billion**. - -## 2. Economic Indicators -- GDP Growth: **2.5%** (2024). -- Inflation Rate: **5.2%**. -- Unemployment Rate: **6.1%**. - -## 3. Market Trends -- Demand is shifting towards electric vehicles, with **37% of EV buyers** aged 18-34. -- EV registrations in urban areas increased by **56%** in 2023. - -## 4. Recommendations -1. Enhance EV infrastructure in urban and rural areas. -2. Implement targeted marketing campaigns focusing on younger demographics. -3. Explore financial incentives for EV purchasers. - -## Conclusion -The dynamic nature of the automotive trade between Portugal and Germany signals a shift towards electric vehicles, necessitating strategical adjustments from stakeholders. diff --git a/packages/server/src/controllers/agents.controller.ts b/packages/server/src/controllers/agents.controller.ts index 5093d10db..1aa268538 100644 --- a/packages/server/src/controllers/agents.controller.ts +++ b/packages/server/src/controllers/agents.controller.ts @@ -33,12 +33,10 @@ import { AgentDeletesRequestDTO, getMessagesFromAgentsDTO, MessageRequest, - MemoryStrategy, } from '@snakagent/core'; import { metrics } from '@snakagent/metrics'; import { FastifyRequest } from 'fastify'; -import { Postgres } from '@snakagent/database'; -import { SnakAgent, SupervisorAgent, BaseAgent } from '@snakagent/agents'; +import { BaseAgent } from '@snakagent/agents'; import { notify, message, @@ -334,7 +332,7 @@ export class AgentsController { const messageRequest: MessageRequest = { agent_id: agent.getAgentConfig().id.toString(), - request: userRequest.request.content ?? '', + content: userRequest.request.content ?? '', }; const action = this.agentService.handleUserRequest( diff --git a/packages/server/src/controllers/gateway.controller.ts b/packages/server/src/controllers/gateway.controller.ts index dd609042f..5b422ec82 100644 --- a/packages/server/src/controllers/gateway.controller.ts +++ b/packages/server/src/controllers/gateway.controller.ts @@ -14,12 +14,11 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { + AddAgentRequestDTO, + AgentDeleteRequestDTO, + AgentRequestDTO, logger, - WebsocketAgentAddRequestDTO, - WebsocketAgentDeleteRequestDTO, - WebsocketAgentRequestDTO, - WebsocketGetAgentsConfigRequestDTO, - WebsocketGetMessagesRequestDTO, + MessageFromAgentIdDTO, } from '@snakagent/core'; import { message } from '@snakagent/database/queries'; import { BaseAgent, EventType, SnakAgent } from '@snakagent/agents'; @@ -46,7 +45,7 @@ export class MyGateway { @SubscribeMessage('agents_request') async handleUserRequest( - @MessageBody() userRequest: WebsocketAgentRequestDTO, + @MessageBody() userRequest: AgentRequestDTO, @ConnectedSocket() client: Socket ): Promise { await ErrorHandler.handleWebSocketError( @@ -60,34 +59,10 @@ export class MyGateway { const userId = ControllerHelpers.getUserIdFromSocket(client); let agent: BaseAgent | undefined; - if (userRequest.request.agent_id === undefined) { - logger.info( - 'Agent ID not provided in request, Using agent Selector to select agent' - ); - - const agentSelector = this.agentFactory.getAgentSelector(); - if (!agentSelector) { - throw new ServerError('E01TA400'); - } - if (!userRequest.request.request) { - throw new ServerError('E01TA400'); // Bad request if no content - } - try { - agent = await agentSelector.execute( - userRequest.request.request, - false, - { userId } - ); - } catch (error) { - logger.error('Error in agentSelector:', error); - throw new ServerError('E01TA400'); - } - } else { - agent = await this.agentFactory.getAgentInstance( - userRequest.request.agent_id, - userId - ); - } + agent = await this.agentFactory.getAgentInstance( + userRequest.request.agent_id, + userId + ); if (!agent) { throw new ServerError('E01TA400'); } @@ -156,7 +131,7 @@ export class MyGateway { @SubscribeMessage('init_agent') async addAgent( - @MessageBody() userRequest: WebsocketAgentAddRequestDTO, + @MessageBody() userRequest: AddAgentRequestDTO, @ConnectedSocket() client: Socket ): Promise { await ErrorHandler.handleWebSocketError( @@ -182,7 +157,7 @@ export class MyGateway { @SubscribeMessage('delete_agent') async deleteAgent( - @MessageBody() userRequest: WebsocketAgentDeleteRequestDTO, + @MessageBody() userRequest: AgentDeleteRequestDTO, @ConnectedSocket() client: Socket ): Promise { await ErrorHandler.handleWebSocketError( @@ -216,10 +191,7 @@ export class MyGateway { } @SubscribeMessage('get_agents') - async getAgents( - @MessageBody() userRequest: WebsocketGetAgentsConfigRequestDTO, - @ConnectedSocket() client: Socket - ): Promise { + async getAgents(@ConnectedSocket() client: Socket): Promise { await ErrorHandler.handleWebSocketError( async () => { logger.info('getAgents called'); @@ -237,9 +209,9 @@ export class MyGateway { ); } - @SubscribeMessage('get_messages') + @SubscribeMessage('get_messages_from_agent') async getMessages( - @MessageBody() userRequest: WebsocketGetMessagesRequestDTO, + @MessageBody() userRequest: MessageFromAgentIdDTO, @ConnectedSocket() client: Socket ): Promise { await ErrorHandler.handleWebSocketError( diff --git a/packages/server/src/interfaces/agent-service.interface.ts b/packages/server/src/interfaces/agent-service.interface.ts index b3ce0f29d..3afa1ae05 100644 --- a/packages/server/src/interfaces/agent-service.interface.ts +++ b/packages/server/src/interfaces/agent-service.interface.ts @@ -1,7 +1,3 @@ -import { MessageRequest } from '@snakagent/core'; -import { IAgent } from './agent.interface.js'; -import { SnakAgent } from '@snakagent/agents'; - export interface AgentExecutionResponse { status: 'success' | 'failure'; data?: unknown; @@ -22,15 +18,3 @@ export interface AgentExecutionCallDataResponse { }; } -export interface IAgentService { - handleUserRequest( - agent: SnakAgent, - userId: string, - userRequest: MessageRequest - ): Promise; - getAgentStatus(agent: IAgent): Promise<{ - isReady: boolean; - walletConnected: boolean; - apiKeyValid: boolean; - }>; -} diff --git a/packages/server/src/interfaces/agent.interface.ts b/packages/server/src/interfaces/agent.interface.ts deleted file mode 100644 index 31fdf3d31..000000000 --- a/packages/server/src/interfaces/agent.interface.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { RpcProvider } from 'starknet'; - -export interface IAgent { - execute( - input: string, - isInterrupted?: boolean, - config?: Record - ): Promise | AsyncGenerator; - - /** - * Returns the agent's Starknet account credentials - * @returns Starknet account credentials - */ - getAccountCredentials(): { - accountPrivateKey: string; - accountPublicKey: string; - }; - - getProvider(): RpcProvider; -} diff --git a/packages/server/src/interfaces/sql_interfaces.ts b/packages/server/src/interfaces/sql_interfaces.ts deleted file mode 100644 index 9cea0394e..000000000 --- a/packages/server/src/interfaces/sql_interfaces.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ConversationSQL { - conversation_id: number; - conversation_name: string; - created_at: Date; - status: string; -} - -export interface MessageSQL { - id: string; - agent_id: string; - user_request: string; - agent_iteration: any; - created_at: Date; -} diff --git a/packages/server/src/interfaces/wallet-service.inferface.ts b/packages/server/src/interfaces/wallet-service.inferface.ts deleted file mode 100644 index 40778eceb..000000000 --- a/packages/server/src/interfaces/wallet-service.inferface.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { AgentRequestDTO } from '@snakagent/core'; -import { IAgent } from './agent.interface.js'; - -export interface AgentExecutionResponse { - status: 'success' | 'failure'; - data?: unknown; - error?: { - message: string; - code?: string; - details?: unknown; - }; -} - -export interface AgentExecutionCallDataResponse { - status: 'success' | 'failure'; - data?: unknown; - error?: { - message: string; - code?: string; - details?: unknown; - }; -} - -export interface IWalletService { - handleUserCalldataRequest( - agent: IAgent, - userRequest: AgentRequestDTO - ): Promise; - getAgentStatus(agent: IAgent): Promise<{ - isReady: boolean; - walletConnected: boolean; - apiKeyValid: boolean; - }>; -} diff --git a/packages/server/src/services/agent.service.ts b/packages/server/src/services/agent.service.ts index 686bd0830..a0388eeec 100644 --- a/packages/server/src/services/agent.service.ts +++ b/packages/server/src/services/agent.service.ts @@ -1,10 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; -import { - IAgentService, - AgentExecutionResponse, -} from '../interfaces/agent-service.interface.js'; -import { IAgent } from '../interfaces/agent.interface.js'; +import { AgentExecutionResponse } from '../interfaces/agent-service.interface.js'; import { AgentConfig, MessageFromAgentIdDTO, @@ -28,7 +24,7 @@ import { redisAgents, agents } from '@snakagent/database/queries'; import { message } from '@snakagent/database/queries'; @Injectable() -export class AgentService implements IAgentService { +export class AgentService { private readonly logger = new Logger(AgentService.name); constructor(private readonly config: ConfigurationService) {} @@ -58,13 +54,13 @@ export class AgentService implements IAgentService { ): Promise { this.logger.debug({ message: 'Processing agent request', - request: userRequest.request, + request: userRequest.content, }); try { let result: any; const user_request: UserRequest = { - request: userRequest.request || '', + request: userRequest.content || '', hitl_threshold: userRequest.hitl_threshold ?? undefined, }; @@ -130,7 +126,7 @@ export class AgentService implements IAgentService { name: error.name, stack: error.stack, }, - request: userRequest.request, + request: userRequest.content, }); if (error instanceof AgentValidationError) { @@ -158,11 +154,11 @@ export class AgentService implements IAgentService { ): AsyncGenerator { this.logger.debug({ message: 'Processing agent request', - request: userRequest.request, + request: userRequest.content, }); try { const user_request: UserRequest = { - request: userRequest.request || '', + request: userRequest.content || '', hitl_threshold: userRequest.hitl_threshold ?? undefined, }; @@ -282,29 +278,4 @@ export class AgentService implements IAgentService { throw error; } } - - async getAgentStatus(agent: IAgent): Promise<{ - isReady: boolean; - walletConnected: boolean; - apiKeyValid: boolean; - }> { - try { - const credentials = agent.getAccountCredentials(); - - // Check if the AI provider API keys are configured - let apiKeyValid = true; // TODO add actual check for API key validity on the agent model - return { - isReady: Boolean(credentials && apiKeyValid), - walletConnected: Boolean(credentials.accountPrivateKey), - apiKeyValid, - }; - } catch (error) { - this.logger.error('Error checking agent status', error); - return { - isReady: false, - walletConnected: false, - apiKeyValid: false, - }; - } - } } diff --git a/tests/src/types.ts b/tests/src/types.ts index 0344dc795..aa5e26f29 100644 --- a/tests/src/types.ts +++ b/tests/src/types.ts @@ -7,7 +7,7 @@ export interface SnakConfig { export interface AgentRequest { request: { content: string; - agent_id?: string; + agent_id: string; }; } From 9787f94dc473ab5b302dc364353dffe9a26547d1 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Fri, 24 Oct 2025 18:35:49 +0100 Subject: [PATCH 02/18] clean-all --- packages/agent/src/agents/core/baseAgent.ts | 5 +- packages/agent/src/agents/core/snakAgent.ts | 4 +- .../agent/src/agents/core/supervisorAgent.ts | 4 +- .../__tests__/task-verifier-graph.test.ts | 2 +- .../graphs/core-graph/supervisor.graph.ts | 6 +- .../manager/memory/memory-db-manager.ts | 2 +- .../agents/graphs/parser/memory/stm-parser.ts | 2 +- .../graphs/parser/tasks/tasks.parser.ts | 2 +- .../graphs/sub-graph/task-executor.graph.ts | 4 +- .../graphs/sub-graph/task-manager.graph.ts | 2 +- .../graphs/sub-graph/task-memory.graph.ts | 6 +- .../graphs/sub-graph/task-verifier.graph.ts | 4 +- .../src/agents/graphs/tools/core.tools.ts | 2 +- .../src/agents/graphs/tools/memory.tool.ts | 2 +- .../graphs/tools/task-executor.tools.ts | 2 +- .../agents/graphs/tools/task-manager.tools.ts | 2 +- .../src/agents/graphs/utils/graph.utils.ts | 2 +- .../src/agents/operators/agentSelector.ts | 116 ----------- .../src/agents/operators/operatorRegistry.ts | 90 --------- .../agent/src/agents/utils/event.utils.ts | 2 +- packages/agent/src/common/index.ts | 0 packages/agent/src/index.ts | 16 +- .../shared/prompts/agents/config.prompts.ts | 18 -- .../shared/prompts/agents/constraints-map.ts | 188 ------------------ .../agent/src/shared/prompts/agents/index.ts | 8 + .../shared/prompts/agents/selector.prompts.ts | 98 --------- .../shared/prompts/agents/snak/core/index.ts | 8 + .../{ => snak/core}/task-executor.prompt.ts | 0 .../{ => snak/core}/task-manager.prompts.ts | 0 .../core}/task-memory-manager.prompt.ts | 0 .../{ => snak/core}/task-verifier.prompts.ts | 0 .../{ => snak/hitl}/hitl-contraint.prompt.ts | 0 .../shared/prompts/agents/snak/hitl/index.ts | 5 + .../src/shared/prompts/agents/snak/index.ts | 6 + .../shared/prompts/agents/supervisor/index.ts | 6 + .../agentConfigurationHelper.prompt.ts | 0 .../agents/supervisor/specialist/index.ts | 6 + .../mcpConfigurationHelper.prompt.ts | 0 .../prompts/core/agentSelectorPrompts.ts | 98 --------- .../shared/prompts/core/configAgentPrompts.ts | 18 -- .../shared/prompts/core/mcpAgentPrompts.ts | 42 ---- .../agent/src/shared/prompts/core/prompts.ts | 72 ------- packages/agent/src/shared/prompts/index.ts | 14 +- .../{graph.schemas.ts => graph.schema.ts} | 0 .../types/agent.type.ts} | 2 +- .../agent/src/shared/types/agents.types.ts | 75 ------- .../types/{config.types.ts => config.type.ts} | 0 .../{database.types.ts => database.type.ts} | 0 .../agent/src/shared/types/error.types.ts | 0 .../types/{event.types.ts => event.type.ts} | 0 .../types/{graph.types.ts => graph.type.ts} | 0 packages/agent/src/shared/types/index.ts | 21 +- .../types/{memory.types.ts => memory.type.ts} | 0 .../{streaming.types.ts => streaming.type.ts} | 4 +- packages/agent/src/shared/types/tools.type.ts | 23 +++ .../agent/src/shared/types/tools.types.ts | 81 -------- packages/agent/src/tools/tools.ts | 161 --------------- .../agent/interfaces/agent.interface.ts | 10 - packages/server/common/errors/agent.errors.ts | 2 +- .../common/errors/application.errors.ts | 2 +- packages/server/common/errors/base.error.ts | 2 +- .../errors/{error.types.ts => error.type.ts} | 0 packages/server/common/errors/index.ts | 2 +- packages/server/common/errors/job.errors.ts | 2 +- .../server/common/errors/starknet.errors.ts | 2 +- packages/server/src/agents.storage.ts | 11 +- .../src/controllers/agents.controller.ts | 35 +--- 67 files changed, 121 insertions(+), 1178 deletions(-) delete mode 100644 packages/agent/src/agents/operators/agentSelector.ts delete mode 100644 packages/agent/src/agents/operators/operatorRegistry.ts delete mode 100644 packages/agent/src/common/index.ts delete mode 100644 packages/agent/src/shared/prompts/agents/config.prompts.ts delete mode 100644 packages/agent/src/shared/prompts/agents/constraints-map.ts create mode 100644 packages/agent/src/shared/prompts/agents/index.ts delete mode 100644 packages/agent/src/shared/prompts/agents/selector.prompts.ts create mode 100644 packages/agent/src/shared/prompts/agents/snak/core/index.ts rename packages/agent/src/shared/prompts/agents/{ => snak/core}/task-executor.prompt.ts (100%) rename packages/agent/src/shared/prompts/agents/{ => snak/core}/task-manager.prompts.ts (100%) rename packages/agent/src/shared/prompts/agents/{ => snak/core}/task-memory-manager.prompt.ts (100%) rename packages/agent/src/shared/prompts/agents/{ => snak/core}/task-verifier.prompts.ts (100%) rename packages/agent/src/shared/prompts/agents/{ => snak/hitl}/hitl-contraint.prompt.ts (100%) create mode 100644 packages/agent/src/shared/prompts/agents/snak/hitl/index.ts create mode 100644 packages/agent/src/shared/prompts/agents/snak/index.ts create mode 100644 packages/agent/src/shared/prompts/agents/supervisor/index.ts rename packages/agent/src/shared/prompts/agents/{ => supervisor/specialist}/agentConfigurationHelper.prompt.ts (100%) create mode 100644 packages/agent/src/shared/prompts/agents/supervisor/specialist/index.ts rename packages/agent/src/shared/prompts/agents/{ => supervisor/specialist}/mcpConfigurationHelper.prompt.ts (100%) delete mode 100644 packages/agent/src/shared/prompts/core/agentSelectorPrompts.ts delete mode 100644 packages/agent/src/shared/prompts/core/configAgentPrompts.ts delete mode 100644 packages/agent/src/shared/prompts/core/mcpAgentPrompts.ts delete mode 100644 packages/agent/src/shared/prompts/core/prompts.ts rename packages/agent/src/shared/schemas/{graph.schemas.ts => graph.schema.ts} (100%) rename packages/agent/src/{types/agent.types.ts => shared/types/agent.type.ts} (87%) delete mode 100644 packages/agent/src/shared/types/agents.types.ts rename packages/agent/src/shared/types/{config.types.ts => config.type.ts} (100%) rename packages/agent/src/shared/types/{database.types.ts => database.type.ts} (100%) delete mode 100644 packages/agent/src/shared/types/error.types.ts rename packages/agent/src/shared/types/{event.types.ts => event.type.ts} (100%) rename packages/agent/src/shared/types/{graph.types.ts => graph.type.ts} (100%) rename packages/agent/src/shared/types/{memory.types.ts => memory.type.ts} (100%) rename packages/agent/src/shared/types/{streaming.types.ts => streaming.type.ts} (88%) create mode 100644 packages/agent/src/shared/types/tools.type.ts delete mode 100644 packages/agent/src/shared/types/tools.types.ts rename packages/server/common/errors/{error.types.ts => error.type.ts} (100%) diff --git a/packages/agent/src/agents/core/baseAgent.ts b/packages/agent/src/agents/core/baseAgent.ts index 62d033557..03b61abcf 100644 --- a/packages/agent/src/agents/core/baseAgent.ts +++ b/packages/agent/src/agents/core/baseAgent.ts @@ -1,6 +1,5 @@ -import { IAgent } from '../../shared/types/agents.types.js'; import { AgentType } from '@enums/agent.enum.js'; -import { ChunkOutput } from '../../shared/types/streaming.types.js'; +import { ChunkOutput } from '../../shared/types/streaming.type.js'; import { PostgresSaver } from '@langchain/langgraph-checkpoint-postgres'; import { CompiledStateGraph } from '@langchain/langgraph'; import { logger, AgentConfig, DatabaseConfigService } from '@snakagent/core'; @@ -8,7 +7,7 @@ import { logger, AgentConfig, DatabaseConfigService } from '@snakagent/core'; /** * Abstract base class for all agents */ -export abstract class BaseAgent implements IAgent { +export abstract class BaseAgent { readonly id: string; readonly type: AgentType; readonly description: string; diff --git a/packages/agent/src/agents/core/snakAgent.ts b/packages/agent/src/agents/core/snakAgent.ts index 063445398..a9c948c7f 100644 --- a/packages/agent/src/agents/core/snakAgent.ts +++ b/packages/agent/src/agents/core/snakAgent.ts @@ -15,11 +15,11 @@ import { import { ChunkOutput, ChunkOutputMetadata, -} from '../../shared/types/streaming.types.js'; +} from '../../shared/types/streaming.type.js'; import { EventType } from '@enums/event.enums.js'; import { isInEnum } from '@enums/utils.js'; import { StreamEvent } from '@langchain/core/tracers/log_stream'; -import { GraphErrorType, UserRequest } from '@stypes/graph.types.js'; +import { GraphErrorType, UserRequest } from '@stypes/graph.type.js'; import { CheckpointerService } from '@agents/graphs/manager/checkpointer/checkpointer.js'; import { notify } from '@snakagent/database/queries'; import { diff --git a/packages/agent/src/agents/core/supervisorAgent.ts b/packages/agent/src/agents/core/supervisorAgent.ts index a79884fc8..5b933db9d 100644 --- a/packages/agent/src/agents/core/supervisorAgent.ts +++ b/packages/agent/src/agents/core/supervisorAgent.ts @@ -4,7 +4,7 @@ import { AgentType, SupervisorNode } from '../../shared/enums/agent.enum.js'; import { ChunkOutput, ChunkOutputMetadata, -} from '../../shared/types/streaming.types.js'; +} from '../../shared/types/streaming.type.js'; import { createSupervisorGraph } from '@agents/graphs/core-graph/supervisor.graph.js'; import { CheckpointerService } from '@agents/graphs/manager/checkpointer/checkpointer.js'; import { @@ -12,7 +12,7 @@ import { AIMessageChunk, HumanMessage, } from '@langchain/core/messages'; -import { GraphErrorType, UserRequest } from '@stypes/graph.types.js'; +import { GraphErrorType, UserRequest } from '@stypes/graph.type.js'; import { EventType } from '@enums/event.enums.js'; import { StreamEvent } from '@langchain/core/tracers/log_stream'; import { StateSnapshot } from '@langchain/langgraph'; diff --git a/packages/agent/src/agents/graphs/__tests__/task-verifier-graph.test.ts b/packages/agent/src/agents/graphs/__tests__/task-verifier-graph.test.ts index 76d775474..c5c1293a5 100644 --- a/packages/agent/src/agents/graphs/__tests__/task-verifier-graph.test.ts +++ b/packages/agent/src/agents/graphs/__tests__/task-verifier-graph.test.ts @@ -3,7 +3,7 @@ // import { ModelSelector } from '../../operators/modelSelector.js'; // import { GraphState, GraphConfigurableAnnotation } from '../graph.js'; // import { TaskVerifierNode } from '../../../shared/enums/agent-modes.enum.js'; -// import { TaskType } from '../../../shared/types/graph.types.js'; +// import { TaskType } from '../../../shared/types/graph.type.js'; // // Mock dependencies // jest.mock('../../operators/modelSelector.js'); diff --git a/packages/agent/src/agents/graphs/core-graph/supervisor.graph.ts b/packages/agent/src/agents/graphs/core-graph/supervisor.graph.ts index fd44d89e8..42d010bd7 100644 --- a/packages/agent/src/agents/graphs/core-graph/supervisor.graph.ts +++ b/packages/agent/src/agents/graphs/core-graph/supervisor.graph.ts @@ -5,7 +5,7 @@ import { Postgres } from '@snakagent/database'; import { AnyZodObject } from 'zod'; import { GraphError } from '../utils/error.utils.js'; import { SupervisorAgent } from '@agents/core/supervisorAgent.js'; -import { skipValidationType } from '@stypes/graph.types.js'; +import { skipValidationType } from '@stypes/graph.type.js'; import { AgentConfig, logger } from '@snakagent/core'; import { GraphState } from './agent.graph.js'; import { initializeDatabase } from '@agents/utils/database.utils.js'; @@ -19,8 +19,8 @@ import { createSupervisor } from '@langchain/langgraph-supervisor'; import { AIMessage, BaseMessage } from '@langchain/core/messages'; import { SUPERVISOR_SYSTEM_PROMPT } from '@prompts/agents/supervisor/supervisor.prompt.js'; import { ChatPromptTemplate } from '@langchain/core/prompts'; -import { AGENT_CONFIGURATION_HELPER_SYSTEM_PROMPT } from '@prompts/agents/agentConfigurationHelper.prompt.js'; -import { MCP_CONFIGURATION_HELPER_SYSTEM_PROMPT } from '@prompts/agents/mcpConfigurationHelper.prompt.js'; +import { AGENT_CONFIGURATION_HELPER_SYSTEM_PROMPT } from '@prompts/agents/supervisor/specialist/agentConfigurationHelper.prompt.js'; +import { MCP_CONFIGURATION_HELPER_SYSTEM_PROMPT } from '@prompts/agents/supervisor/specialist/mcpConfigurationHelper.prompt.js'; import { Annotation, messagesStateReducer } from '@langchain/langgraph'; const SupervisorStateAnnotation = Annotation.Root({ diff --git a/packages/agent/src/agents/graphs/manager/memory/memory-db-manager.ts b/packages/agent/src/agents/graphs/manager/memory/memory-db-manager.ts index 2ca78ea62..d5372862a 100644 --- a/packages/agent/src/agents/graphs/manager/memory/memory-db-manager.ts +++ b/packages/agent/src/agents/graphs/manager/memory/memory-db-manager.ts @@ -15,7 +15,7 @@ import { MemoryItem, MemoryOperationResult, SemanticMemoryContext, -} from '../../../../shared/types/memory.types.js'; +} from '../../../../shared/types/memory.type.js'; export const embeddingModel = new CustomHuggingFaceEmbeddings({ model: 'Xenova/all-MiniLM-L6-v2', diff --git a/packages/agent/src/agents/graphs/parser/memory/stm-parser.ts b/packages/agent/src/agents/graphs/parser/memory/stm-parser.ts index ad21b82d3..70c6a9d2a 100644 --- a/packages/agent/src/agents/graphs/parser/memory/stm-parser.ts +++ b/packages/agent/src/agents/graphs/parser/memory/stm-parser.ts @@ -7,7 +7,7 @@ import { } from '@langchain/core/messages'; import { STMManager } from '@lib/memory/index.js'; import { logger } from '@snakagent/core'; -import { STMContext } from '@stypes/memory.types.js'; +import { STMContext } from '@stypes/memory.type.js'; function formatAiMessagetoXML( message: AIMessage | AIMessageChunk, diff --git a/packages/agent/src/agents/graphs/parser/tasks/tasks.parser.ts b/packages/agent/src/agents/graphs/parser/tasks/tasks.parser.ts index 7873dfa1c..fd3d04da6 100644 --- a/packages/agent/src/agents/graphs/parser/tasks/tasks.parser.ts +++ b/packages/agent/src/agents/graphs/parser/tasks/tasks.parser.ts @@ -1,4 +1,4 @@ -import { TaskType } from '@stypes/graph.types.js'; +import { TaskType } from '@stypes/graph.type.js'; export function tasks_parser( tasks: TaskType[], diff --git a/packages/agent/src/agents/graphs/sub-graph/task-executor.graph.ts b/packages/agent/src/agents/graphs/sub-graph/task-executor.graph.ts index c08c073f6..21cf69f6e 100644 --- a/packages/agent/src/agents/graphs/sub-graph/task-executor.graph.ts +++ b/packages/agent/src/agents/graphs/sub-graph/task-executor.graph.ts @@ -37,7 +37,7 @@ import { STRING_LIMITS, } from '../constants/execution-constants.js'; import { v4 as uuidv4 } from 'uuid'; -import { ThoughtsSchemaType } from '@schemas/graph.schemas.js'; +import { ThoughtsSchemaType } from '@schemas/graph.schema.js'; import { TaskType, Memories, @@ -54,7 +54,7 @@ import { TASK_EXECUTOR_MEMORY_LONG_TERM_MEMORY_PROMPT, TASK_EXECUTOR_MEMORY_RAG_PROMPT, TASK_EXECUTOR_SYSTEM_PROMPT, -} from '@prompts/agents/task-executor.prompt.js'; +} from '@prompts/agents/snak/core/task-executor.prompt.js'; import { TaskExecutorToolRegistry } from '../tools/task-executor.tools.js'; import { GraphError } from '../utils/error.utils.js'; diff --git a/packages/agent/src/agents/graphs/sub-graph/task-manager.graph.ts b/packages/agent/src/agents/graphs/sub-graph/task-manager.graph.ts index 50e75f35c..4fa6edb7f 100644 --- a/packages/agent/src/agents/graphs/sub-graph/task-manager.graph.ts +++ b/packages/agent/src/agents/graphs/sub-graph/task-manager.graph.ts @@ -39,7 +39,7 @@ import { TASK_MANAGER_MEMORY_PROMPT_TASK_HISTORY, TASK_MANAGER_MEMORY_RAG_PROMPT, TASK_MANAGER_SYSTEM_PROMPT, -} from '@prompts/agents/task-manager.prompts.js'; +} from '@prompts/agents/snak/core/task-manager.prompts.js'; import { TaskManagerToolRegistryInstance } from '../tools/task-manager.tools.js'; import { GraphError } from '../utils/error.utils.js'; import { tasks_parser } from '../parser/tasks/tasks.parser.js'; diff --git a/packages/agent/src/agents/graphs/sub-graph/task-memory.graph.ts b/packages/agent/src/agents/graphs/sub-graph/task-memory.graph.ts index 602b13952..2b3907af0 100644 --- a/packages/agent/src/agents/graphs/sub-graph/task-memory.graph.ts +++ b/packages/agent/src/agents/graphs/sub-graph/task-memory.graph.ts @@ -6,7 +6,7 @@ import { ltmSchemaType, createLtmSchemaMemorySchema, HolisticMemoryContext, -} from '../../../shared/types/memory.types.js'; +} from '../../../shared/types/memory.type.js'; import { getCurrentTask, getRetrieveMemoryRequestFromGraph, @@ -43,12 +43,12 @@ import { StepType, TaskType, ToolCallType, -} from '../../../shared/types/graph.types.js'; +} from '../../../shared/types/graph.type.js'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { TASK_MEMORY_MANAGER_HUMAN_PROMPT, TASK_MEMORY_MANAGER_SYSTEM_PROMPT, -} from '@prompts/agents/task-memory-manager.prompt.js'; +} from '@prompts/agents/snak/core/task-memory-manager.prompt.js'; import { memory } from '@snakagent/database/queries'; import { EXECUTOR_CORE_TOOLS } from './task-executor.graph.js'; import { GraphError } from '../utils/error.utils.js'; diff --git a/packages/agent/src/agents/graphs/sub-graph/task-verifier.graph.ts b/packages/agent/src/agents/graphs/sub-graph/task-verifier.graph.ts index bc84757aa..19fe66f58 100644 --- a/packages/agent/src/agents/graphs/sub-graph/task-verifier.graph.ts +++ b/packages/agent/src/agents/graphs/sub-graph/task-verifier.graph.ts @@ -34,11 +34,11 @@ import { GraphError } from '../utils/error.utils.js'; import { TASK_VERIFICATION_CONTEXT_PROMPT, TASK_VERIFIER_SYSTEM_PROMPT, -} from '@prompts/agents/task-verifier.prompts.js'; +} from '@prompts/agents/snak/core/task-verifier.prompts.js'; import { TaskVerificationSchema, TaskVerificationSchemaType, -} from '@schemas/graph.schemas.js'; +} from '@schemas/graph.schema.js'; import { formatSTMToXML } from '../parser/memory/stm-parser.js'; export class TaskVerifierGraph { diff --git a/packages/agent/src/agents/graphs/tools/core.tools.ts b/packages/agent/src/agents/graphs/tools/core.tools.ts index c81e0c341..c4357d7f2 100644 --- a/packages/agent/src/agents/graphs/tools/core.tools.ts +++ b/packages/agent/src/agents/graphs/tools/core.tools.ts @@ -1,7 +1,7 @@ import { DynamicStructuredTool, tool } from '@langchain/core/tools'; import { AnyZodObject } from 'zod'; import { BaseToolRegistry } from './base-tool-registry.js'; -import { ThoughtsSchema } from '@schemas/graph.schemas.js'; +import { ThoughtsSchema } from '@schemas/graph.schema.js'; const endTask = (): string => { return 'Task ended successfully'; diff --git a/packages/agent/src/agents/graphs/tools/memory.tool.ts b/packages/agent/src/agents/graphs/tools/memory.tool.ts index d927d3d0e..fef48a1f1 100644 --- a/packages/agent/src/agents/graphs/tools/memory.tool.ts +++ b/packages/agent/src/agents/graphs/tools/memory.tool.ts @@ -8,7 +8,7 @@ import { retrieveMemoryFromStepIdType, retrieveMemoryFromTaskId, retrieveMemoryFromTaskIdType, -} from '@stypes/memory.types.js'; +} from '@stypes/memory.type.js'; import { AgentConfig, logger } from '@snakagent/core'; import { memory } from '@snakagent/database/queries'; import { embeddingModel } from '../manager/memory/memory-db-manager.js'; diff --git a/packages/agent/src/agents/graphs/tools/task-executor.tools.ts b/packages/agent/src/agents/graphs/tools/task-executor.tools.ts index 0e33ee19d..b616326e5 100644 --- a/packages/agent/src/agents/graphs/tools/task-executor.tools.ts +++ b/packages/agent/src/agents/graphs/tools/task-executor.tools.ts @@ -2,7 +2,7 @@ import { DynamicStructuredTool, tool } from '@langchain/core/tools'; import { BaseToolRegistry } from './base-tool-registry.js'; import { AnyZodObject } from 'zod'; import { AgentConfig } from '@snakagent/core'; -import { ThoughtsSchema } from '@schemas/graph.schemas.js'; +import { ThoughtsSchema } from '@schemas/graph.schema.js'; export class TaskExecutorToolRegistry extends BaseToolRegistry { constructor() { diff --git a/packages/agent/src/agents/graphs/tools/task-manager.tools.ts b/packages/agent/src/agents/graphs/tools/task-manager.tools.ts index 3922cf160..403f9f4d1 100644 --- a/packages/agent/src/agents/graphs/tools/task-manager.tools.ts +++ b/packages/agent/src/agents/graphs/tools/task-manager.tools.ts @@ -1,7 +1,7 @@ import { DynamicStructuredTool, tool } from '@langchain/core/tools'; import { BaseToolRegistry } from './base-tool-registry.js'; import { AnyZodObject } from 'zod'; -import { TaskSchema } from '@schemas/graph.schemas.js'; +import { TaskSchema } from '@schemas/graph.schema.js'; export class TaskManagerToolRegistry extends BaseToolRegistry { constructor() { diff --git a/packages/agent/src/agents/graphs/utils/graph.utils.ts b/packages/agent/src/agents/graphs/utils/graph.utils.ts index b4cff1037..dd40fb37d 100644 --- a/packages/agent/src/agents/graphs/utils/graph.utils.ts +++ b/packages/agent/src/agents/graphs/utils/graph.utils.ts @@ -31,7 +31,7 @@ import { HITL_CONSTRAINT_LEVEL_2, HITL_CONSTRAINT_LEVEL_3, HITL_CONSTRAINT_LEVEL_4, -} from '@prompts/agents/hitl-contraint.prompt.js'; +} from '@prompts/agents/snak/hitl/hitl-contraint.prompt.js'; // --- Response Generators --- export function createMaxIterationsResponse( graph_step: number, diff --git a/packages/agent/src/agents/operators/agentSelector.ts b/packages/agent/src/agents/operators/agentSelector.ts deleted file mode 100644 index 0bc3efc37..000000000 --- a/packages/agent/src/agents/operators/agentSelector.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { BaseAgent } from '../core/baseAgent.js'; -import { AgentConfig, logger } from '@snakagent/core'; -import { SnakAgent } from '../core/snakAgent.js'; -import { agentSelectorPromptContent } from '../../shared/prompts/core/prompts.js'; -import { AgentType } from '@enums/agent-modes.enum.js'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { AgentConfigResolver, AgentBuilder } from '../../types/agent.types.js'; - -export interface AgentInfo { - name: string; - description: string; -} - -/** - * AgentSelector analyzes user queries and determines which specialized agent should handle each request. - * It supports both explicit agent mentions and AI-powered agent selection based on query context. - */ -export class AgentSelector extends BaseAgent { - private agentConfigResolver: AgentConfigResolver; - private agentBuilder: AgentBuilder; - private model: BaseChatModel; - - constructor( - agentConfigResolver: AgentConfigResolver, - agentBuilder: AgentBuilder, - agentConfig: AgentConfig.Runtime - ) { - super('agent-selector', AgentType.OPERATOR, agentConfig); - this.agentConfigResolver = agentConfigResolver; - this.agentBuilder = agentBuilder; - this.model = agentConfig.graph.model; - if (!this.model) { - logger.warn( - 'AgentSelector: No model provided, selection capabilities will be limited' - ); - } - } - - public async init(): Promise { - logger.debug('AgentSelector: Initialized'); - } - - public async execute( - input: string, - _isInterrupted?: boolean, - config?: Record - ): Promise { - try { - if (!config) { - throw new Error('AgentSelector: config parameter is required'); - } - if (!config.userId) { - throw new Error( - 'AgentSelector: userId is required in config parameter' - ); - } - const model = this.model; - - const userId = config.userId as string; - logger.debug( - `AgentSelector: Fetching agent configs for user ${userId} from Redis` - ); - - // Fetch agent configurations from Redis via the resolver - const agentConfigs = await this.agentConfigResolver(userId); - - logger.debug(`AgentSelector: Fetching agent configs for user ${userId}`); - - if (agentConfigs.length === 0) { - throw new Error('No agents found for user ' + userId); - } - - // Build agent info map for the prompt from configurations - const userAgentInfo = new Map(); - for (const agentConfig of agentConfigs) { - userAgentInfo.set( - agentConfig.profile.name, - agentConfig.profile.description || 'No description available' - ); - } - - const result = await model.invoke( - agentSelectorPromptContent(userAgentInfo, input) - ); - logger.debug('AgentSelector result:', result); - - if (typeof result.content === 'string') { - const r_trim = result.content.trim(); - const selectedConfig = agentConfigs.find( - (cfg) => cfg.profile.name.toLowerCase() === r_trim.toLowerCase() - ); - if (selectedConfig) { - logger.debug( - `AgentSelector: Selected agent ${r_trim}, building instance...` - ); - // Build the agent ONLY for the selected config - const agent = await this.agentBuilder(selectedConfig); - logger.debug(`AgentSelector: Successfully built agent ${r_trim}`); - if (agent instanceof SnakAgent) { - return agent; - } - throw new Error('Built agent is not an instance of SnakAgent'); - } else { - logger.warn( - `AgentSelector: No matching agent found for response "${r_trim}"` - ); - throw new Error('No matching agent found'); - } - } else { - throw new Error('AgentSelector did not return a valid string response'); - } - } catch (error) { - throw new Error('AgentSelector execution failed: ' + error.message); - } - } -} diff --git a/packages/agent/src/agents/operators/operatorRegistry.ts b/packages/agent/src/agents/operators/operatorRegistry.ts deleted file mode 100644 index bb999ebbe..000000000 --- a/packages/agent/src/agents/operators/operatorRegistry.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { logger } from '@snakagent/core'; -import { IAgent } from '../../shared/types/agents.types.js'; - -/** - * Registry for managing available operator agents - */ -export class OperatorRegistry { - private static instance: OperatorRegistry; - private registry: Map = new Map(); - - private constructor() {} - - public static getInstance(): OperatorRegistry { - if (!OperatorRegistry.instance) { - OperatorRegistry.instance = new OperatorRegistry(); - } - return OperatorRegistry.instance; - } - - /** - * Register a new operator agent - * @param agentId - Unique identifier for the agent - * @param agent - The agent instance to register - */ - public register(agentId: string, agent: IAgent): void { - if (this.registry.has(agentId)) { - logger.warn( - `OperatorRegistry: Operator agent "${agentId}" already registered. Overwriting.` - ); - } - this.registry.set(agentId, agent); - logger.debug(`OperatorRegistry: Registered operator agent "${agentId}"`); - } - - /** - * Unregister an operator agent - * @param agentId - Unique identifier of the agent to unregister - * @returns True if the agent was successfully unregistered, false otherwise - */ - public unregister(agentId: string): boolean { - const result = this.registry.delete(agentId); - if (result) { - logger.debug( - `OperatorRegistry: Unregistered operator agent "${agentId}"` - ); - } else { - logger.warn( - `OperatorRegistry: Failed to unregister "${agentId}". Agent not found.` - ); - } - return result; - } - - /** - * Get an operator agent by ID - * @param agentId - Unique identifier of the agent - * @returns The agent instance or undefined if not found - */ - public getAgent(agentId: string): IAgent | undefined { - return this.registry.get(agentId); - } - - /** - * Get all registered operator agents - * @returns Record containing all registered agents with their IDs as keys - */ - public getAllAgents(): Record { - const agents: Record = {}; - this.registry.forEach((agent, id) => { - agents[id] = agent; - }); - return agents; - } - - /** - * Get the number of registered agents - * @returns The count of registered agents - */ - public size(): number { - return this.registry.size; - } - - /** - * Clear all registered agents from the registry - */ - public clear(): void { - this.registry.clear(); - logger.debug('OperatorRegistry: Cleared all operator agents'); - } -} diff --git a/packages/agent/src/agents/utils/event.utils.ts b/packages/agent/src/agents/utils/event.utils.ts index 770b5f7ab..48b59dab3 100644 --- a/packages/agent/src/agents/utils/event.utils.ts +++ b/packages/agent/src/agents/utils/event.utils.ts @@ -1,5 +1,5 @@ import { EventType } from '@enums/event.enums.js'; -import { LangGraphEvent } from '../../shared/types/event.types.js'; +import { LangGraphEvent } from '../../shared/types/event.type.js'; export function isEventType( event: LangGraphEvent, diff --git a/packages/agent/src/common/index.ts b/packages/agent/src/common/index.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/agent/src/index.ts b/packages/agent/src/index.ts index 4050abfaf..6182eab62 100644 --- a/packages/agent/src/index.ts +++ b/packages/agent/src/index.ts @@ -13,26 +13,21 @@ export { initializeToolsList } from './tools/tools.js'; export type { ChunkOutput, ChunkOutputMetadata, -} from './shared/types/streaming.types.js'; +} from './shared/types/streaming.type.js'; // Graph mode exports export { createGraph } from './agents/graphs/core-graph/agent.graph.js'; // Agent operators -export { AgentSelector } from './agents/operators/agentSelector.js'; export * from './agents/operators/supervisor/index.js'; // Agent types -export type { AgentConfigResolver, AgentBuilder } from './types/agent.types.js'; - -// Tool-related exports export type { - SnakAgentInterface, - StarknetTool, -} from './shared/types/tools.types.js'; + AgentConfigResolver, + AgentBuilder, +} from './shared/types/agent.type.js'; -export { createAllowedTools, registerTools } from './tools/tools.js'; -export type { SnakToolRegistry } from './tools/tools.js'; +// Tool-related exports // Consolidated exports from new structure export * from './shared/types/index.js'; // All types @@ -41,4 +36,3 @@ export * from './shared/lib/memory/index.js'; // Memory utilities (if index.ts e export * from './shared/lib/token/index.js'; // Token tracking (if index.ts exists) export * from './shared/prompts/index.js'; // All prompts // Legacy exports for backward compatibility -export type { IAgent } from './shared/types/agents.types.js'; diff --git a/packages/agent/src/shared/prompts/agents/config.prompts.ts b/packages/agent/src/shared/prompts/agents/config.prompts.ts deleted file mode 100644 index 1661aa63b..000000000 --- a/packages/agent/src/shared/prompts/agents/config.prompts.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const configurationAgentSystemPrompt = (): string => { - return `You are a Configuration Agent specialized in managing agent configurations through intelligent tool selection. - -Core Operations: -- CREATE: Use create_agent for "create", "add", "new", "make" requests -- READ: Use read_agent for "get", "show", "view", "find" specific agent requests -- UPDATE: Use update_agent for "modify", "change", "update", "edit", "rename" requests -- DELETE: Use delete_agent for "delete", "remove", "destroy" requests -- LIST: Use list_agents for "list", "show all", "get all" requests - -Parameter Extraction Guidelines: -- Extract agent names from quotes or context: "Agent Name" → identifier: "Agent Name" -- Use "name" search by default, "id" only when explicitly provided -- For updates: map user intent to specific fields (name, description, group, etc.) -- Be precise with parameter values - extract exactly what user specifies - -Always confirm what operation you're performing and provide clear feedback about results.`; -}; diff --git a/packages/agent/src/shared/prompts/agents/constraints-map.ts b/packages/agent/src/shared/prompts/agents/constraints-map.ts deleted file mode 100644 index 0d53f3ed8..000000000 --- a/packages/agent/src/shared/prompts/agents/constraints-map.ts +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Comprehensive constraint mapping for autonomous AI systems - * Based on AutoGPT, SuperAGI, and BabyAGI patterns - */ - -export const AUTONOMOUS_AI_CONSTRAINTS = { - // Core Operational Constraints - INDEPENDENT_DECISION_MAKING: - 'Your decisions must always be made independently without seeking user assistance', - LEVERAGE_LLM_STRENGTHS: - 'Play to your strengths as an LLM and pursue simple strategies with no legal complications', - COMMAND_RESTRICTION: - 'These are the ONLY commands you can use. Any action you perform must be possible through one of these commands', - TOOL_EXCLUSIVITY: - "Exclusively use the tools listed in double quotes e.g. 'tool name'", - DECISION_BASED_ON_DATA_TOOLS: - 'Make decisions based solely on available data and tools', - SUBSEQUENT_TASKS: `Subsequent tasks depend on what you'll discover in past task`, - DECISITION_SAFEST_POSSIBLE: `If uncertain about a decision, choose the safest option`, - NEVER_WAIT_HUMAN: `Never wait for human approval before proceeding`, - THINK_CHECK_AND_ADAPT: `Think "check and adapt" not "build from scratch"`, - TOOL_END_TASK: `CRITICAL USE TOOL END_TASK WHEN YOU RESOLVE YOUR GOAL. DON'T OVERTHINKING`, - TOOL_END_TASK_IF: `CRITICAL USE TOOL END_TASK IMMEDIATELY IF YOUT ENCOUNTER ANY ERROR OR BLOCKING SITUATION'`, - DONT_OVERTHINK: `Don't overthink, only resolve your goal`, - WORKING_MEMORY_BLOCKED: `If you see that you are looping on same step/tool in WORKING_MEMORY with the same result, you are blocked and must use end_task`, - - // Task Verification Constraints - OBJECTIVE_ANALYSIS_REQUIRED: - 'Conduct objective analysis based on concrete evidence and measurable outcomes', - EVIDENCE_BASED_ASSESSMENT: - 'Base all assessments on actual tool results, outputs, and observable evidence', - STRICT_COMPLETION_CRITERIA: - 'Apply strict criteria - tasks are only complete when all original objectives are fully met', - DETAILED_REASONING_MANDATORY: - 'Provide detailed, step-by-step reasoning for all completion assessments', - // Output Format Constraints - JSON_RESPONSE_MANDATORY: 'YOU MUST ALWAYS RESPOND WITH A JSON OBJECT.', - TOOL_INVOCATION_REQUIRED: - 'YOU MUST ALSO INVOKE A TOOL! (when use_functions_api is enabled)', - VALID_JSON_ONLY: - 'Respond with only valid JSON conforming to the following schema', - STRUCTURED_OUTPUT_FIELDS: - 'Must include required fields: thoughts (text, reasoning, plan, criticism, speak) and tool (name, args)', - NO_ADDITIONAL_PROPERTIES: - 'additionalProperties: false (no extra fields allowed in JSON response)', - NO_EXTRA_TEXT: - 'Ensure response is valid JSON with no additional text outside of the JSON', - - // Ethical and Legal Constraints - NO_ILLEGAL_PLANS: 'Do not suggest illegal or unethical plans or strategies', - BUDGET_AWARENESS: 'Take reasonable budgetary limits into account', - SAFETY_BOUNDARIES: 'Maintain legal and ethical guardrails in all operations', - - // Technical Constraints - REQUIRED_PARAMETERS: - 'Function parameters that has no default value and not optional typed has to be provided', - NO_MISSING_ARGUMENTS: 'No missing argument is allowed', - FUNCTION_SIGNATURE_MATCH: - 'Always choose a function call from the list of function signatures', - TYPE_MATCHING: - 'Always provide the complete argument with type matching the required jsonschema signature', - - // Performance and Efficiency Constraints - COST_AWARENESS: 'Every tool has a cost, so be smart and efficient', - CONTINUOUS_REVIEW: - 'Continuously review and analyze your actions to ensure you are performing to the best of your abilities', - SELF_CRITICISM: - 'Constructively self-criticize your big-picture behavior constantly', - REFLECTION_BASED_IMPROVEMENT: - 'Reflect on past decisions and strategies to refine your approach', - RESOURCE_EFFICIENCY: 'Be mindful of computational and resource costs', - - // Task Management Constraints - NO_DUPLICATE_TASKS: - "Don't create any task if it is already covered in incomplete or completed tasks", - REMOVE_UNNECESSARY_TASKS: 'Remove tasks if they are unnecessary or duplicate', - GOAL_ALIGNED_TASKS: - "Remove tasks if they don't help in achieving the main goal", - NO_GOAL_DEVIATION: - 'Ensure new tasks are not deviated from completing the goal', - MAX_INITIAL_STEPS: 'Maximum of 3 steps in initial task sequence', - - // Execution Flow Constraints - INSTRUCTION_BASED_FLOW: - 'Use instruction to decide the flow of execution and decide the next steps', - TASK_COMPLETION_RECOGNITION: - "If you have completed all your tasks or reached end state, make sure to use the 'finish' tool", - AUTONOMOUS_TERMINATION: - 'Recognize when tasks are complete and terminate appropriately', - - // Error Handling and State Management - EXCEPTION_MANAGEMENT: 'Manage exceptions and unexpected states gracefully', - STATE_AWARENESS: 'Maintain awareness of current system state and progress', - ERROR_RECOVERY: 'Implement appropriate error recovery mechanisms', - - // Goal Alignment - OBJECTIVE_FOCUS: 'All actions must serve the main objective', - STRATEGIC_ALIGNMENT: 'Ensure all strategies align with primary goals', - PURPOSE_DRIVEN_ACTIONS: 'Every action should contribute to goal achievement', -} as const; - -/** - * Categorized constraint groups for easier access - */ -export const CONSTRAINT_CATEGORIES = { - OPERATIONAL: [ - 'INDEPENDENT_DECISION_MAKING', - 'LEVERAGE_LLM_STRENGTHS', - 'COMMAND_RESTRICTION', - 'TOOL_EXCLUSIVITY', - ], - - OUTPUT_FORMAT: [ - 'JSON_RESPONSE_MANDATORY', - 'TOOL_INVOCATION_REQUIRED', - 'VALID_JSON_ONLY', - 'STRUCTURED_OUTPUT_FIELDS', - 'NO_ADDITIONAL_PROPERTIES', - 'NO_EXTRA_TEXT', - ], - - ETHICAL_LEGAL: ['NO_ILLEGAL_PLANS', 'BUDGET_AWARENESS', 'SAFETY_BOUNDARIES'], - - TECHNICAL: [ - 'REQUIRED_PARAMETERS', - 'NO_MISSING_ARGUMENTS', - 'FUNCTION_SIGNATURE_MATCH', - 'TYPE_MATCHING', - ], - - PERFORMANCE: [ - 'COST_AWARENESS', - 'CONTINUOUS_REVIEW', - 'SELF_CRITICISM', - 'REFLECTION_BASED_IMPROVEMENT', - 'RESOURCE_EFFICIENCY', - ], - - TASK_MANAGEMENT: [ - 'NO_DUPLICATE_TASKS', - 'REMOVE_UNNECESSARY_TASKS', - 'GOAL_ALIGNED_TASKS', - 'NO_GOAL_DEVIATION', - 'MAX_INITIAL_STEPS', - ], - - EXECUTION_FLOW: [ - 'INSTRUCTION_BASED_FLOW', - 'TASK_COMPLETION_RECOGNITION', - 'AUTONOMOUS_TERMINATION', - ], - - ERROR_HANDLING: ['EXCEPTION_MANAGEMENT', 'STATE_AWARENESS', 'ERROR_RECOVERY'], - - GOAL_ALIGNMENT: [ - 'OBJECTIVE_FOCUS', - 'STRATEGIC_ALIGNMENT', - 'PURPOSE_DRIVEN_ACTIONS', - ], -} as const; - -/** - * Helper function to get constraints by category - */ -export function getConstraintsByCategory( - category: keyof typeof CONSTRAINT_CATEGORIES -): string[] { - return CONSTRAINT_CATEGORIES[category].map( - (key) => - AUTONOMOUS_AI_CONSTRAINTS[key as keyof typeof AUTONOMOUS_AI_CONSTRAINTS] - ); -} - -/** - * Helper function to get all constraints as an array - */ -export function getAllConstraints(): string[] { - return Object.values(AUTONOMOUS_AI_CONSTRAINTS); -} - -/** - * Helper function to get constraint by key - */ -export function getConstraint( - key: keyof typeof AUTONOMOUS_AI_CONSTRAINTS -): string { - return AUTONOMOUS_AI_CONSTRAINTS[key]; -} diff --git a/packages/agent/src/shared/prompts/agents/index.ts b/packages/agent/src/shared/prompts/agents/index.ts new file mode 100644 index 000000000..225a4d73d --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/index.ts @@ -0,0 +1,8 @@ +/** + * Re-export all agent prompts + */ + +export * from './core.prompts.js'; +export * from './ltm-summarization.prompt.js'; +export * from './snak/index.js'; +export * from './supervisor/index.js'; diff --git a/packages/agent/src/shared/prompts/agents/selector.prompts.ts b/packages/agent/src/shared/prompts/agents/selector.prompts.ts deleted file mode 100644 index 108724269..000000000 --- a/packages/agent/src/shared/prompts/agents/selector.prompts.ts +++ /dev/null @@ -1,98 +0,0 @@ -export interface AgentSelectionPromptParams { - query: string; - agentDescriptions: string; -} - -export interface ClarificationData { - possibleAgents: string[]; - missingInfo: string; - clarificationQuestion: string; -} - -export const agentSelectionSystemPrompt = ( - agentDescriptions: string -): string => { - // Add array brackets if they're missing - const jsonString = agentDescriptions.trim().startsWith('[') - ? agentDescriptions - : `[${agentDescriptions}]`; - - try { - const agents = JSON.parse(jsonString); - - const formatAgents = (type: string) => { - return agents - .filter((agent: any) => agent.type === type) - .map( - (agent: any) => - `ID: ${agent.id} -Name: ${agent.name} -Description: ${agent.description} -` - ) - .join('\n\n'); - }; - - return `You are an agent selector responsible for analyzing user queries and determining which specialized agent should handle each request. - -Key responsibilities: -1. Analyze user intent and requirements -2. Match capabilities with available agents -3. Provide clear agent selection or request clarification when needed -4. Consider agent names, types, and descriptions when making selections - -Important Selection Rules: -- For any requests related to managing, modifying, or viewing agent configurations (including names, descriptions, or settings), select the configuration-agent -- For requests related to managing MCP (Model Context Protocol) servers, their configurations, or tools (including adding, removing, updating, or listing MCPs), select the mcp-agent -- For blockchain-specific operations, select the corresponding blockchain RPC agent -- For SNAK-related operations, select the appropriate SNAK agent - -AVAILABLE AGENTS BY TYPE: - -OPERATOR AGENTS: -[ -${formatAgents('operator')} -] - -SNAK AGENTS: -[ -${formatAgents('snak')} -] -INSTRUCTIONS: -1. First, understand what the user is trying to accomplish: - - Is it a configuration change? → configuration-agent - - Is it an MCP server operation? → mcp-agent - - Is it a blockchain operation? → corresponding blockchain RPC agent - - Is it a SNAK operation? → corresponding SNAK agent - -2. Your response must ONLY contain the ID of the selected agent (the exact string from the "id" field). - Do not include any explanations, analysis, or other text. - -3. If the query doesn't match any available agent's capabilities, respond with "NO_MATCHING_AGENT". - -Always prioritize accuracy and specificity in your selections.`; - } catch (error) { - console.error('Error parsing agent descriptions:', error); - return 'Error: Unable to parse agent descriptions'; - } -}; - -export const agentSelectionPrompt = (query: string): string => { - return query; -}; - -export const noMatchingAgentMessage = (): string => { - return "I don't have an agent that can handle this specific request. Could you clarify what you're trying to do?"; -}; - -export const defaultClarificationMessage = (): string => { - return 'I need more information to select the appropriate agent. Could you provide more details about what you need?'; -}; - -export const errorFallbackMessage = (): string => { - return 'I encountered an issue understanding your request. Could you rephrase it or provide more details about what you need help with?'; -}; - -export const noValidAgentMessage = (): string => { - return "I couldn't identify which agent should handle your request. Could you describe more precisely what you need help with?"; -}; diff --git a/packages/agent/src/shared/prompts/agents/snak/core/index.ts b/packages/agent/src/shared/prompts/agents/snak/core/index.ts new file mode 100644 index 000000000..eace57272 --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/snak/core/index.ts @@ -0,0 +1,8 @@ +/** + * Re-export all SNAK core prompts + */ + +export * from './task-executor.prompt.js'; +export * from './task-manager.prompts.js'; +export * from './task-memory-manager.prompt.js'; +export * from './task-verifier.prompts.js'; diff --git a/packages/agent/src/shared/prompts/agents/task-executor.prompt.ts b/packages/agent/src/shared/prompts/agents/snak/core/task-executor.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/task-executor.prompt.ts rename to packages/agent/src/shared/prompts/agents/snak/core/task-executor.prompt.ts diff --git a/packages/agent/src/shared/prompts/agents/task-manager.prompts.ts b/packages/agent/src/shared/prompts/agents/snak/core/task-manager.prompts.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/task-manager.prompts.ts rename to packages/agent/src/shared/prompts/agents/snak/core/task-manager.prompts.ts diff --git a/packages/agent/src/shared/prompts/agents/task-memory-manager.prompt.ts b/packages/agent/src/shared/prompts/agents/snak/core/task-memory-manager.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/task-memory-manager.prompt.ts rename to packages/agent/src/shared/prompts/agents/snak/core/task-memory-manager.prompt.ts diff --git a/packages/agent/src/shared/prompts/agents/task-verifier.prompts.ts b/packages/agent/src/shared/prompts/agents/snak/core/task-verifier.prompts.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/task-verifier.prompts.ts rename to packages/agent/src/shared/prompts/agents/snak/core/task-verifier.prompts.ts diff --git a/packages/agent/src/shared/prompts/agents/hitl-contraint.prompt.ts b/packages/agent/src/shared/prompts/agents/snak/hitl/hitl-contraint.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/hitl-contraint.prompt.ts rename to packages/agent/src/shared/prompts/agents/snak/hitl/hitl-contraint.prompt.ts diff --git a/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts b/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts new file mode 100644 index 000000000..d5284fbec --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts @@ -0,0 +1,5 @@ +/** + * Re-export all HITL prompts + */ + +export * from './hitl-contraint.prompt.js'; diff --git a/packages/agent/src/shared/prompts/agents/snak/index.ts b/packages/agent/src/shared/prompts/agents/snak/index.ts new file mode 100644 index 000000000..cf78fa9b9 --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/snak/index.ts @@ -0,0 +1,6 @@ +/** + * Re-export all SNAK prompts + */ + +export * from './core/index.js'; +export * from './hitl/index.js'; diff --git a/packages/agent/src/shared/prompts/agents/supervisor/index.ts b/packages/agent/src/shared/prompts/agents/supervisor/index.ts new file mode 100644 index 000000000..5fe576fca --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/supervisor/index.ts @@ -0,0 +1,6 @@ +/** + * Re-export all supervisor prompts + */ + +export * from './supervisor.prompt.js'; +export * from './specialist/index.js'; diff --git a/packages/agent/src/shared/prompts/agents/agentConfigurationHelper.prompt.ts b/packages/agent/src/shared/prompts/agents/supervisor/specialist/agentConfigurationHelper.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/agentConfigurationHelper.prompt.ts rename to packages/agent/src/shared/prompts/agents/supervisor/specialist/agentConfigurationHelper.prompt.ts diff --git a/packages/agent/src/shared/prompts/agents/supervisor/specialist/index.ts b/packages/agent/src/shared/prompts/agents/supervisor/specialist/index.ts new file mode 100644 index 000000000..6ad58823a --- /dev/null +++ b/packages/agent/src/shared/prompts/agents/supervisor/specialist/index.ts @@ -0,0 +1,6 @@ +/** + * Re-export all specialist prompts + */ + +export * from './agentConfigurationHelper.prompt.js'; +export * from './mcpConfigurationHelper.prompt.js'; diff --git a/packages/agent/src/shared/prompts/agents/mcpConfigurationHelper.prompt.ts b/packages/agent/src/shared/prompts/agents/supervisor/specialist/mcpConfigurationHelper.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/mcpConfigurationHelper.prompt.ts rename to packages/agent/src/shared/prompts/agents/supervisor/specialist/mcpConfigurationHelper.prompt.ts diff --git a/packages/agent/src/shared/prompts/core/agentSelectorPrompts.ts b/packages/agent/src/shared/prompts/core/agentSelectorPrompts.ts deleted file mode 100644 index 108724269..000000000 --- a/packages/agent/src/shared/prompts/core/agentSelectorPrompts.ts +++ /dev/null @@ -1,98 +0,0 @@ -export interface AgentSelectionPromptParams { - query: string; - agentDescriptions: string; -} - -export interface ClarificationData { - possibleAgents: string[]; - missingInfo: string; - clarificationQuestion: string; -} - -export const agentSelectionSystemPrompt = ( - agentDescriptions: string -): string => { - // Add array brackets if they're missing - const jsonString = agentDescriptions.trim().startsWith('[') - ? agentDescriptions - : `[${agentDescriptions}]`; - - try { - const agents = JSON.parse(jsonString); - - const formatAgents = (type: string) => { - return agents - .filter((agent: any) => agent.type === type) - .map( - (agent: any) => - `ID: ${agent.id} -Name: ${agent.name} -Description: ${agent.description} -` - ) - .join('\n\n'); - }; - - return `You are an agent selector responsible for analyzing user queries and determining which specialized agent should handle each request. - -Key responsibilities: -1. Analyze user intent and requirements -2. Match capabilities with available agents -3. Provide clear agent selection or request clarification when needed -4. Consider agent names, types, and descriptions when making selections - -Important Selection Rules: -- For any requests related to managing, modifying, or viewing agent configurations (including names, descriptions, or settings), select the configuration-agent -- For requests related to managing MCP (Model Context Protocol) servers, their configurations, or tools (including adding, removing, updating, or listing MCPs), select the mcp-agent -- For blockchain-specific operations, select the corresponding blockchain RPC agent -- For SNAK-related operations, select the appropriate SNAK agent - -AVAILABLE AGENTS BY TYPE: - -OPERATOR AGENTS: -[ -${formatAgents('operator')} -] - -SNAK AGENTS: -[ -${formatAgents('snak')} -] -INSTRUCTIONS: -1. First, understand what the user is trying to accomplish: - - Is it a configuration change? → configuration-agent - - Is it an MCP server operation? → mcp-agent - - Is it a blockchain operation? → corresponding blockchain RPC agent - - Is it a SNAK operation? → corresponding SNAK agent - -2. Your response must ONLY contain the ID of the selected agent (the exact string from the "id" field). - Do not include any explanations, analysis, or other text. - -3. If the query doesn't match any available agent's capabilities, respond with "NO_MATCHING_AGENT". - -Always prioritize accuracy and specificity in your selections.`; - } catch (error) { - console.error('Error parsing agent descriptions:', error); - return 'Error: Unable to parse agent descriptions'; - } -}; - -export const agentSelectionPrompt = (query: string): string => { - return query; -}; - -export const noMatchingAgentMessage = (): string => { - return "I don't have an agent that can handle this specific request. Could you clarify what you're trying to do?"; -}; - -export const defaultClarificationMessage = (): string => { - return 'I need more information to select the appropriate agent. Could you provide more details about what you need?'; -}; - -export const errorFallbackMessage = (): string => { - return 'I encountered an issue understanding your request. Could you rephrase it or provide more details about what you need help with?'; -}; - -export const noValidAgentMessage = (): string => { - return "I couldn't identify which agent should handle your request. Could you describe more precisely what you need help with?"; -}; diff --git a/packages/agent/src/shared/prompts/core/configAgentPrompts.ts b/packages/agent/src/shared/prompts/core/configAgentPrompts.ts deleted file mode 100644 index 1661aa63b..000000000 --- a/packages/agent/src/shared/prompts/core/configAgentPrompts.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const configurationAgentSystemPrompt = (): string => { - return `You are a Configuration Agent specialized in managing agent configurations through intelligent tool selection. - -Core Operations: -- CREATE: Use create_agent for "create", "add", "new", "make" requests -- READ: Use read_agent for "get", "show", "view", "find" specific agent requests -- UPDATE: Use update_agent for "modify", "change", "update", "edit", "rename" requests -- DELETE: Use delete_agent for "delete", "remove", "destroy" requests -- LIST: Use list_agents for "list", "show all", "get all" requests - -Parameter Extraction Guidelines: -- Extract agent names from quotes or context: "Agent Name" → identifier: "Agent Name" -- Use "name" search by default, "id" only when explicitly provided -- For updates: map user intent to specific fields (name, description, group, etc.) -- Be precise with parameter values - extract exactly what user specifies - -Always confirm what operation you're performing and provide clear feedback about results.`; -}; diff --git a/packages/agent/src/shared/prompts/core/mcpAgentPrompts.ts b/packages/agent/src/shared/prompts/core/mcpAgentPrompts.ts deleted file mode 100644 index f31748d8c..000000000 --- a/packages/agent/src/shared/prompts/core/mcpAgentPrompts.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* *** Depracted *** */ - -/** - * System prompt for the MCP agent that manages Model Context Protocol servers - * @returns The system prompt string for the MCP agent - */ -export const mcpAgentSystemPrompt = - () => `You are a specialized MCP (Model Context Protocol) Agent responsible for managing MCP servers and their tools in the system. - -Your primary responsibilities include: -1. Managing MCP server configurations (add, remove, update, list) -2. Monitoring MCP server status and health -3. Managing and organizing MCP tools -4. Ensuring proper integration of MCP servers with the agent system - -When handling requests: -- Always validate inputs before performing operations -- Maintain consistent configuration formats -- Ensure proper error handling and logging -- Keep track of MCP server states and connections -- Provide clear feedback on operation results - -Use the available tools to: -- List and inspect MCP servers -- Manage MCP server configurations -- View and organize MCP tools -- Monitor MCP server status - -Remember: -- MCP servers are crucial for extending agent capabilities -- Configuration changes should be handled carefully -- Always maintain proper security practices -- Keep configurations well-documented - -Respond to user queries by: -1. Understanding the requested operation -2. Validating inputs and current state -3. Using appropriate tools to perform the operation -4. Providing clear feedback on results -5. Handling any errors gracefully - -Your goal is to ensure smooth operation and management of MCP servers while maintaining system stability and security.`; diff --git a/packages/agent/src/shared/prompts/core/prompts.ts b/packages/agent/src/shared/prompts/core/prompts.ts deleted file mode 100644 index 3ac7468ba..000000000 --- a/packages/agent/src/shared/prompts/core/prompts.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { MessageContent } from '@langchain/core/messages'; -import { AgentConfig } from '@snakagent/core'; - -export const modelSelectorSystemPrompt = (nextStepsSection: string): string => { - return `You are a model selector responsible for analyzing user queries and determining which AI model should handle each request.\n -${nextStepsSection ? "Focus primarily on the 'Next planned actions' which represents upcoming tasks.\n" : ''} -SELECTION CRITERIA: -- Select 'fast' for simple, focused tasks that involve a single action or basic operations. -- Select 'smart' for complex reasoning, creativity, or tasks that might take multiple steps to complete. -- Select 'cheap' for non-urgent, simple tasks that don't require sophisticated reasoning. - -PRIORITY RULES: -- Priority is on simplicity - if the task appears to be trying to do too much at once, select 'smart'. -- If the task is properly broken down into one simple step, prefer 'fast' or 'cheap'. - -RESPONSE FORMAT: -Respond with only one word: 'fast', 'smart', or 'cheap'.`; -}; - -export const modelSelectorRules = ( - nextStepsSection: string, - analysisContent: string -) => { - return ` - Analyze this User Input and determine which AI model should handle it. - - ${nextStepsSection ? "Focus primarily on the 'Next planned actions' which represents upcoming tasks." : ''} - Select 'fast' for simple, focused tasks that involve a single action or basic operations. - Select 'smart' for complex reasoning, creativity, or tasks that might take multiple steps to complete. - Select 'cheap' for non-urgent, simple tasks that don't require sophisticated reasoning. - - Priority is on simplicity - if the task appears to be trying to do too much at once, select 'smart'. - If the task is properly broken down into one simple step, prefer 'fast' or 'cheap'. - - Respond with only one word: 'fast', 'smart', or 'cheap'. - - User Input: - ${analysisContent}`; -}; - -export const finalAnswerRules = (finalAnswer: MessageContent) => { - return ` - I've received your final answer: "${finalAnswer}"\n\nBased on the history of your actions and your objectives, decide what to do next. You can either continue with another task or refine your previous solution. - `; -}; - -export const agentSelectorPromptContent = ( - agentInfo: Map, - input: string -) => { - return `You are an Agent Router responsible for analyzing requests and selecting the most qualified agent. - - ROUTING RULES: - 1. Analyze the request to identify: domain, required skills, task type, and complexity. - 2. Match request requirements with agent capabilities from their descriptions. - 3. Select the agent with the highest alignment to the request's primary needs. - 4. Consider specialist agents over generalists when expertise matches exactly. - 5. For multi-domain requests, prioritize the agent covering the main objective. - 6. Respond with the agent's name only, without additional text or formatting never break this rules. - - AGENT DESCRIPTIONS: - ${Array.from(agentInfo) - .map(([name, description]) => `- **${name}**: ${description}`) - .join('\n')} - - USER REQUEST: - ${input} - RESPONSE FORMAT: - response with the agent_name. - Example of response: "agent_1" - `; -}; diff --git a/packages/agent/src/shared/prompts/index.ts b/packages/agent/src/shared/prompts/index.ts index 69234602a..7a00ccafb 100644 --- a/packages/agent/src/shared/prompts/index.ts +++ b/packages/agent/src/shared/prompts/index.ts @@ -2,15 +2,5 @@ * Re-export all prompts from the prompts directory */ -// Core prompts -export * from './core/prompts.js'; -export * from './core/mcpAgentPrompts.js'; - -// Agent prompts (prefer these over core versions to avoid conflicts) -export * from './agents/selector.prompts.js'; - -// Task Manager prompts -export * from './agents/task-manager.prompts.js'; -export * from './agents/task-executor.prompt.js'; -export * from './agents/task-verifier.prompts.js'; -export * from './agents/task-memory-manager.prompt.js'; +// All agent prompts (includes core, snak, supervisor, and their submodules) +export * from './agents/index.js'; diff --git a/packages/agent/src/shared/schemas/graph.schemas.ts b/packages/agent/src/shared/schemas/graph.schema.ts similarity index 100% rename from packages/agent/src/shared/schemas/graph.schemas.ts rename to packages/agent/src/shared/schemas/graph.schema.ts diff --git a/packages/agent/src/types/agent.types.ts b/packages/agent/src/shared/types/agent.type.ts similarity index 87% rename from packages/agent/src/types/agent.types.ts rename to packages/agent/src/shared/types/agent.type.ts index 2f091e1ee..2743f548e 100644 --- a/packages/agent/src/types/agent.types.ts +++ b/packages/agent/src/shared/types/agent.type.ts @@ -1,5 +1,5 @@ import { AgentConfig } from '@snakagent/core'; -import { SnakAgent } from '../agents/core/snakAgent.js'; +import { SnakAgent } from '../../agents/core/snakAgent.js'; import { BaseAgent } from '@agents/core/baseAgent.js'; /** diff --git a/packages/agent/src/shared/types/agents.types.ts b/packages/agent/src/shared/types/agents.types.ts deleted file mode 100644 index 1c290c252..000000000 --- a/packages/agent/src/shared/types/agents.types.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { AgentType } from '@enums/agent.enum.js'; -import { Annotation } from '@langchain/langgraph'; -import { ChunkOutput } from './streaming.types.js'; - -/** - * Base interface for all agents - */ -/** - * Base interface for all agents in the system - */ -export interface IAgent { - /** - * Unique identifier of the agent - */ - readonly id: string; - - /** - * Type of agent - */ - readonly type: AgentType; - readonly description?: string; - - /** - * Initializes the agent - */ - init(): Promise; - - /** - * Executes an action with the agent - * @param input Input to process - * @param config Optional configuration - */ - execute( - input: any, - isInterrupted?: boolean, - - config?: Record - ): Promise | AsyncGenerator; - - /** - * Optional method to clean up resources used by the agent. - */ - dispose?: () => Promise; -} - -/** - * Orchestrator types for different agent operations - */ -export type TASK_MANAGER_ORCHESTRATOR = - | 'task_manager' - | 'task_manager_validator' - | 'evolve_from_history' - | 'plan_revision'; - -export type AGENT_EXECUTOR = 'exec_validator' | 'executor'; - -export type MEMORY_ORCHESTRATOR = 'memory_manager'; - -/** - * Interactive configurable annotation for LangGraph - */ -export const InteractiveConfigurableAnnotation = Annotation.Root({ - max_graph_steps: Annotation({ - reducer: (x, y) => y, - default: () => 15, - }), - short_term_memory: Annotation({ - reducer: (x, y) => y, - default: () => 15, - }), - memorySize: Annotation({ - reducer: (x, y) => y, - default: () => 20, - }), -}); diff --git a/packages/agent/src/shared/types/config.types.ts b/packages/agent/src/shared/types/config.type.ts similarity index 100% rename from packages/agent/src/shared/types/config.types.ts rename to packages/agent/src/shared/types/config.type.ts diff --git a/packages/agent/src/shared/types/database.types.ts b/packages/agent/src/shared/types/database.type.ts similarity index 100% rename from packages/agent/src/shared/types/database.types.ts rename to packages/agent/src/shared/types/database.type.ts diff --git a/packages/agent/src/shared/types/error.types.ts b/packages/agent/src/shared/types/error.types.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/agent/src/shared/types/event.types.ts b/packages/agent/src/shared/types/event.type.ts similarity index 100% rename from packages/agent/src/shared/types/event.types.ts rename to packages/agent/src/shared/types/event.type.ts diff --git a/packages/agent/src/shared/types/graph.types.ts b/packages/agent/src/shared/types/graph.type.ts similarity index 100% rename from packages/agent/src/shared/types/graph.types.ts rename to packages/agent/src/shared/types/graph.type.ts diff --git a/packages/agent/src/shared/types/index.ts b/packages/agent/src/shared/types/index.ts index 3f3d37842..2039fd8e7 100644 --- a/packages/agent/src/shared/types/index.ts +++ b/packages/agent/src/shared/types/index.ts @@ -2,26 +2,23 @@ * Re-export all types from the types directory */ -// Agent-related types -export * from './agents.types.js'; - // Memory-related types -export * from './memory.types.js'; +export * from './memory.type.js'; // Tool-related types -export * from './tools.types.js'; +export * from './tools.type.js'; // Graph-related types -export * from './graph.types.js'; +export * from './graph.type.js'; // Configuration types -export * from './config.types.js'; +export * from './config.type.js'; // Database types -export * from './database.types.js'; - -// Error Types -export * from './error.types.js'; +export * from './database.type.js'; // Streaming types -export * from './streaming.types.js'; +export * from './streaming.type.js'; + +// Agent-related types +export * from './agent.type.js'; diff --git a/packages/agent/src/shared/types/memory.types.ts b/packages/agent/src/shared/types/memory.type.ts similarity index 100% rename from packages/agent/src/shared/types/memory.types.ts rename to packages/agent/src/shared/types/memory.type.ts diff --git a/packages/agent/src/shared/types/streaming.types.ts b/packages/agent/src/shared/types/streaming.type.ts similarity index 88% rename from packages/agent/src/shared/types/streaming.types.ts rename to packages/agent/src/shared/types/streaming.type.ts index 6d3485836..29faff7ba 100644 --- a/packages/agent/src/shared/types/streaming.types.ts +++ b/packages/agent/src/shared/types/streaming.type.ts @@ -1,7 +1,7 @@ import { GraphNode, SupervisorNode } from '@enums/agent.enum.js'; import { EventType } from '@enums/event.enums.js'; -import { ToolCall } from './tools.types.js'; -import { GraphErrorType } from './graph.types.js'; +import { ToolCall } from './tools.type.js'; +import { GraphErrorType } from './graph.type.js'; export interface ChunkOutputMetadata { execution_mode?: string; diff --git a/packages/agent/src/shared/types/tools.type.ts b/packages/agent/src/shared/types/tools.type.ts new file mode 100644 index 000000000..66cd47a3c --- /dev/null +++ b/packages/agent/src/shared/types/tools.type.ts @@ -0,0 +1,23 @@ +import { Id } from "@snakagent/core"; + +export interface ToolArgs { + [key: string]: string | number | boolean; +} + +// Base type without required id +export type ToolCallBase = { + name: string; + args: ToolArgs; + type?: 'tool_call'; +}; + +// Type with required id +export type ToolCallWithId = { + name: string; + args: ToolArgs; + id: string; + type?: 'tool_call'; +}; + +export type ToolCall = + HasId extends Id.Id ? ToolCallWithId : ToolCallBase; diff --git a/packages/agent/src/shared/types/tools.types.ts b/packages/agent/src/shared/types/tools.types.ts deleted file mode 100644 index 14380c697..000000000 --- a/packages/agent/src/shared/types/tools.types.ts +++ /dev/null @@ -1,81 +0,0 @@ -export interface ToolArgs { - [key: string]: string | number | boolean; -} - -// Base type without required id -export type ToolCallBase = { - name: string; - args: ToolArgs; - type?: 'tool_call'; -}; - -// Type with required id -export type ToolCallWithId = { - name: string; - args: ToolArgs; - id: string; - type?: 'tool_call'; -}; - -export type ToolCall = - HasId extends Id.Id ? ToolCallWithId : ToolCallBase; - -import { z as Zod } from 'zod'; -import { RpcProvider } from 'starknet'; -import { AgentConfig, Id } from '@snakagent/core'; -import { RagAgent } from '@agents/operators/ragAgent.js'; -import { DatabaseCredentials } from './database.types.js'; - -/** - * @interface SnakAgentInterface - * @description Interface for the Starknet agent - * @property {() => { accountPublicKey: string; accountPrivateKey: string; }} getAccountCredentials - Function to get the account credentials - * @property {() => { signature: string; }} getSignature - Function to get the signature - * @property {() => RpcProvider} getProvider - Function to get the provider - * @property {() => AgentConfig} getAgentConfig - Function to get the agent configuration - * @property {() => PostgresAdaptater[]} getDatabase - Function to get the database - * @property {(database_name: string) => Promise} connectDatabase - Function to connect to a database - * @property {(database_name: string) => Promise} createDatabase - Function to create a database - * @property {(name: string) => PostgresAdaptater | undefined} getDatabaseByName - Function to get a database by name - */ - -export interface SnakAgentInterface { - getDatabaseCredentials: () => DatabaseCredentials; - getProvider: () => RpcProvider; - getAgentConfig: () => AgentConfig.Runtime; - getRagAgent: () => RagAgent | null; -} - -/** - * @interface StarknetTool - * @description Interface for the Starknet tool - * @property {string} name - The name of the tool - * @property {string} plugins - The plugins for the tool - * @property {string} description - The description of the tool - * @property {Zod.AnyZodObject} schema - The schema for the tool - * @property {string} responseFormat - The response format for the tool - * @property {(agent: SnakAgentInterface, params: any, plugins_manager?: any) => Promise} execute - Function to execute the tool - */ -export interface StarknetTool

{ - name: string; - plugins: string; - description: string; - schema?: Zod.AnyZodObject; - responseFormat?: string; - execute: ( - agent: SnakAgentInterface, - params: P, - plugins_manager?: any - ) => Promise; -} - -/** - * Signature Tool Interface - */ -export interface SignatureTool

{ - name: string; - categorie?: string; - description: string; - schema?: object; - execute: (params: P) => Promise; -} diff --git a/packages/agent/src/tools/tools.ts b/packages/agent/src/tools/tools.ts index f419a5a1c..f569ee79f 100644 --- a/packages/agent/src/tools/tools.ts +++ b/packages/agent/src/tools/tools.ts @@ -2,17 +2,10 @@ import { DynamicStructuredTool, StructuredTool, Tool, - tool, } from '@langchain/core/tools'; import { logger, AgentConfig, supervisorAgentConfig } from '@snakagent/core'; -import { metrics } from '@snakagent/metrics'; import { AnyZodObject } from 'zod'; import { MCP_CONTROLLER } from '@services/mcp/src/mcp.js'; -import { - SnakAgentInterface, - StarknetTool, -} from '../shared/types/tools.types.js'; -import { MemoryToolRegistry } from '@agents/graphs/tools/memory.tool.js'; import { CoreToolRegistry } from '@agents/graphs/tools/core.tools.js'; import { getSupervisorConfigTools } from '@agents/operators/supervisor/supervisorTools.js'; @@ -85,157 +78,3 @@ export async function initializeToolsList( toolsList.push(...coreRegistry.getTools()); return toolsList; } - -/** - * @class SnakToolRegistry - * @description Class for the Starknet tool registry - * @property {StarknetTool[]} tools - The tools - * @method {void} registerTool - Method to register a tool - * @method {Promise} createAllowedTools - Method to create allowed tools - * - */ -export class SnakToolRegistry { - private static tools: StarknetTool[] = []; - - static registerTool

(tool: StarknetTool

): void { - this.tools.push(tool); - } - - /** - * @static - * @function clearTools - * @description Clears all registered tools - */ - static clearTools(): void { - this.tools = []; - } - - /** - * @static - * @async - * @function createAllowedTools - * @description Creates allowed tools - * @param {SnakAgentInterface} agent - The Starknet agent - * @param {string[]} allowed_tools - The allowed tools - * @returns {Promise} The allowed tools - */ - static async createAllowedTools( - agent: SnakAgentInterface, - allowed_tools: string[] = [] - ) { - // Clear existing tools before registering new ones - this.clearTools(); - - if (!allowed_tools || allowed_tools.length === 0) { - logger.warn('SnakToolRegistry: No External tools allowed'); - logger.warn('SnakToolRegistry: No External tools allowed'); - return []; - } - - await registerTools(agent, allowed_tools, this.tools); - return this.tools.map(({ name, description, schema, execute }) => - tool(async (params: any) => execute(agent, params), { - name, - description, - ...(schema && { schema }), - }) - ); - } -} - -/** - * @async - * @function registerTools - * @description Registers tools - * @param {SnakAgentInterface} agent - The Starknet agent - * @param {string[]} allowed_tools - The allowed tools - * @param {StarknetTool[]} tools - The tools - * @throws {Error} Throws an error if the tools cannot be registered - */ -export const registerTools = async ( - agent: SnakAgentInterface, - allowed_tools: string[] = [], - tools: StarknetTool[] -): Promise => { - try { - if (!allowed_tools || allowed_tools.length === 0) { - logger.warn('registerTools: No tools to register'); - return; - } - - let index = 0; - await Promise.all( - allowed_tools.map(async (tool) => { - if (!tool) { - logger.warn( - `registerTools: Skipping undefined tool at index ${index}` - ); - return false; - } - - index = index + 1; - - try { - const imported_tool = await import( - `@snakagent/plugin-${tool}/dist/index.js` - ); - if (typeof imported_tool.registerTools !== 'function') { - logger.warn( - `Plugin ${tool} does not export a registerTools function` - ); - return false; - } - const tools_new = new Array(); - await imported_tool.registerTools(tools_new, agent); - const agentId = agent.getAgentConfig().id; - - for (const tool of tools_new) { - metrics.agentToolUseCount( - agentId.toString(), - 'autonomous', - tool.name - ); - metrics.agentToolUseCount( - agentId.toString(), - 'autonomous', - tool.name - ); - } - - tools.push(...tools_new); - return true; - } catch (error) { - logger.error(`Error loading plugin ${tool}: ${error}`); - return false; - } - }) - ); - if (tools.length === 0) { - logger.warn('No tools registered'); - } - } catch (error) { - logger.error(`Error registering tools: ${error}`); - } -}; - -/** - * @async - * @function createAllowedTools - * @description Creates allowed tools - * @param {SnakAgentInterface} agent - The Starknet agent - * @param {string[]} allowed_tools - The allowed tools - * @throws {Error} Throws an error if the allowed tools cannot be created - */ -export const createAllowedTools = async ( - agent: SnakAgentInterface, - allowed_tools: string[] = [] -): Promise[]> => { - if (!allowed_tools || allowed_tools.length === 0) { - logger.warn('No External tools allowed'); - logger.warn('No External tools allowed'); - return []; - } - return SnakToolRegistry.createAllowedTools(agent, allowed_tools); -}; - -export default SnakToolRegistry; diff --git a/packages/core/src/common/agent/interfaces/agent.interface.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts index 8aa9fca35..de64f1002 100644 --- a/packages/core/src/common/agent/interfaces/agent.interface.ts +++ b/packages/core/src/common/agent/interfaces/agent.interface.ts @@ -1,18 +1,8 @@ import { RpcProvider } from 'starknet'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { z as Zod } from 'zod'; export type Modify = Omit & R; -export interface StarknetTool

{ - name: string; - plugins: string; - description: string; - schema?: Zod.AnyZodObject; - response_format?: string; - execute: (agent: any, params: P, plugins_manager?: any) => Promise; -} - export interface ModelConfig { provider: string; model_name: string; diff --git a/packages/server/common/errors/agent.errors.ts b/packages/server/common/errors/agent.errors.ts index 976c4fda2..c10d74490 100644 --- a/packages/server/common/errors/agent.errors.ts +++ b/packages/server/common/errors/agent.errors.ts @@ -1,5 +1,5 @@ import { BaseError } from './base.error.js'; -import { ErrorType, ErrorMetadata } from './error.types.js'; +import { ErrorType, ErrorMetadata } from './error.type.js'; export class AgentExecutionError extends BaseError { constructor(message: string, metadata?: ErrorMetadata) { diff --git a/packages/server/common/errors/application.errors.ts b/packages/server/common/errors/application.errors.ts index 38862436b..5751b1103 100644 --- a/packages/server/common/errors/application.errors.ts +++ b/packages/server/common/errors/application.errors.ts @@ -1,5 +1,5 @@ import { BaseError } from './base.error.js'; -import { ErrorType, ErrorMetadata } from './error.types.js'; +import { ErrorType, ErrorMetadata } from './error.type.js'; export class ValidationError extends BaseError { constructor(message: string, metadata?: ErrorMetadata) { diff --git a/packages/server/common/errors/base.error.ts b/packages/server/common/errors/base.error.ts index f322b383b..2b85bee97 100644 --- a/packages/server/common/errors/base.error.ts +++ b/packages/server/common/errors/base.error.ts @@ -1,4 +1,4 @@ -import { ErrorType, ErrorMetadata, ErrorResponse } from './error.types.js'; +import { ErrorType, ErrorMetadata, ErrorResponse } from './error.type.js'; export class BaseError extends Error { constructor( diff --git a/packages/server/common/errors/error.types.ts b/packages/server/common/errors/error.type.ts similarity index 100% rename from packages/server/common/errors/error.types.ts rename to packages/server/common/errors/error.type.ts diff --git a/packages/server/common/errors/index.ts b/packages/server/common/errors/index.ts index f169bb0b0..6d39a7ced 100644 --- a/packages/server/common/errors/index.ts +++ b/packages/server/common/errors/index.ts @@ -1,4 +1,4 @@ -export * from './error.types.js'; +export * from './error.type.js'; export * from './base.error.js'; export * from './application.errors.js'; export * from './starknet.errors.js'; diff --git a/packages/server/common/errors/job.errors.ts b/packages/server/common/errors/job.errors.ts index 37f1c7f03..b7fda9cbd 100644 --- a/packages/server/common/errors/job.errors.ts +++ b/packages/server/common/errors/job.errors.ts @@ -1,5 +1,5 @@ import { BaseError } from './base.error.js'; -import { ErrorType, ErrorMetadata } from './error.types.js'; +import { ErrorType, ErrorMetadata } from './error.type.js'; export class JobNotFoundError extends BaseError { constructor(jobId: string, metadata?: ErrorMetadata) { diff --git a/packages/server/common/errors/starknet.errors.ts b/packages/server/common/errors/starknet.errors.ts index e033e963e..ce5b49605 100644 --- a/packages/server/common/errors/starknet.errors.ts +++ b/packages/server/common/errors/starknet.errors.ts @@ -1,5 +1,5 @@ import { BaseError } from './base.error.js'; -import { ErrorType, ErrorMetadata } from './error.types.js'; +import { ErrorType, ErrorMetadata } from './error.type.js'; export class StarknetTransactionError extends BaseError { constructor(message: string, metadata?: ErrorMetadata) { diff --git a/packages/server/src/agents.storage.ts b/packages/server/src/agents.storage.ts index 3efcf4b83..d0e1b223d 100644 --- a/packages/server/src/agents.storage.ts +++ b/packages/server/src/agents.storage.ts @@ -16,7 +16,6 @@ import { // Add this import if ModelSelectorConfig is exported from @snakagent/core import DatabaseStorage from '../common/database/database.storage.js'; import { - AgentSelector, AgentConfigResolver, SnakAgent, TASK_EXECUTOR_SYSTEM_PROMPT, @@ -37,7 +36,6 @@ const logger = new Logger('AgentStorage'); */ @Injectable() export class AgentStorage implements OnModuleInit { - private agentSelector: AgentSelector; private initialized: boolean = false; private initializationPromise: Promise | null = null; private agentValidationService: AgentValidationService; @@ -184,14 +182,7 @@ export class AgentStorage implements OnModuleInit { } } } - - public getAgentSelector(): AgentSelector { - if (!this.agentSelector) { - throw new Error('AgentSelector is not initialized'); - } - return this.agentSelector; - } - + public async getModelFromUser(userId: string): Promise { if (!userId || userId.length === 0) { throw new Error('User ID is required to fetch model configuration'); diff --git a/packages/server/src/controllers/agents.controller.ts b/packages/server/src/controllers/agents.controller.ts index 1aa268538..fdbac62a5 100644 --- a/packages/server/src/controllers/agents.controller.ts +++ b/packages/server/src/controllers/agents.controller.ts @@ -296,36 +296,13 @@ export class AgentsController { const route = this.reflector.get('path', this.handleUserRequest); let agent: BaseAgent | undefined = undefined; - if (userRequest.request.agent_id === undefined) { - logger.info( - 'Agent ID not provided in request, Using agent Selector to select agent' - ); - if ( - !userRequest.request.content || - userRequest.request.content?.length === 0 - ) { - throw new ServerError('E01TA400'); // Bad request if no content - } - const agentSelector = this.agentFactory.getAgentSelector(); - agent = await agentSelector.execute(userRequest.request.content, false, { - userId, - }); - if (agent) { - const agentId = agent.getAgentConfig().id; - await ControllerHelpers.verifyAgentConfigOwnership( - this.agentFactory, - agentId, - userId - ); - } - } else { - agent = await ControllerHelpers.verifyAgentOwnership( - this.agentFactory, - userRequest.request.agent_id, - userId - ); - } + agent = await ControllerHelpers.verifyAgentOwnership( + this.agentFactory, + userRequest.request.agent_id, + userId + ); + if (!agent) { throw new ServerError('E01TA400'); } From e7a534642ba5ec62937bbccd6fdd85bf074fe317 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 10:38:15 +0000 Subject: [PATCH 03/18] clean-all --- packages/agent/src/shared/types/tools.type.ts | 2 +- packages/core/src/common/constant/agents.constants.ts | 5 ++++- packages/core/src/common/constant/default-agent.constant.ts | 6 +++++- packages/server/src/agents.storage.ts | 2 +- packages/server/src/interfaces/agent-service.interface.ts | 1 - 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/agent/src/shared/types/tools.type.ts b/packages/agent/src/shared/types/tools.type.ts index 66cd47a3c..d1f8aad3c 100644 --- a/packages/agent/src/shared/types/tools.type.ts +++ b/packages/agent/src/shared/types/tools.type.ts @@ -1,4 +1,4 @@ -import { Id } from "@snakagent/core"; +import { Id } from '@snakagent/core'; export interface ToolArgs { [key: string]: string | number | boolean; diff --git a/packages/core/src/common/constant/agents.constants.ts b/packages/core/src/common/constant/agents.constants.ts index b40d65720..20332c9ed 100644 --- a/packages/core/src/common/constant/agents.constants.ts +++ b/packages/core/src/common/constant/agents.constants.ts @@ -1,4 +1,7 @@ -import { AgentConfig, MemoryStrategy } from '@common/agent/interfaces/agent.interface.js'; +import { + AgentConfig, + MemoryStrategy, +} from '@common/agent/interfaces/agent.interface.js'; /** * Agent Selector Configuration diff --git a/packages/core/src/common/constant/default-agent.constant.ts b/packages/core/src/common/constant/default-agent.constant.ts index 5c097085a..6f55abf58 100644 --- a/packages/core/src/common/constant/default-agent.constant.ts +++ b/packages/core/src/common/constant/default-agent.constant.ts @@ -1,4 +1,8 @@ -import { AgentConfig, MemoryStrategy, ModelConfig } from '@common/agent/interfaces/agent.interface.js'; +import { + AgentConfig, + MemoryStrategy, + ModelConfig, +} from '@common/agent/interfaces/agent.interface.js'; export const DEFAULT_AGENT_MODEL: ModelConfig = { provider: 'gemini', diff --git a/packages/server/src/agents.storage.ts b/packages/server/src/agents.storage.ts index d0e1b223d..cafadc188 100644 --- a/packages/server/src/agents.storage.ts +++ b/packages/server/src/agents.storage.ts @@ -182,7 +182,7 @@ export class AgentStorage implements OnModuleInit { } } } - + public async getModelFromUser(userId: string): Promise { if (!userId || userId.length === 0) { throw new Error('User ID is required to fetch model configuration'); diff --git a/packages/server/src/interfaces/agent-service.interface.ts b/packages/server/src/interfaces/agent-service.interface.ts index 3afa1ae05..b0aaae8b8 100644 --- a/packages/server/src/interfaces/agent-service.interface.ts +++ b/packages/server/src/interfaces/agent-service.interface.ts @@ -17,4 +17,3 @@ export interface AgentExecutionCallDataResponse { details?: unknown; }; } - From c180cce418ab8e9ba065a2a3f33075fd5aabdeb5 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 11:00:33 +0000 Subject: [PATCH 04/18] clean version + add only gemini and better env handling system --- .../agent/src/__mocks__/@snakagent/core.ts | 8 - .../agents/operators/mcp-agent/mcpAgent.ts | 276 -------- .../operators/mcp-agent/mcpAgentTools.ts | 627 ------------------ .../services/mcp/src/__tests__/mcp.spec.ts | 2 +- .../mcp/src/{mcp.ts => mcp.service.ts} | 0 packages/agent/src/tools/tools.ts | 2 +- packages/server/config/configuration.ts | 31 +- packages/server/config/env.validation.ts | 7 +- packages/server/src/utils/agents.utils.ts | 53 +- 9 files changed, 46 insertions(+), 960 deletions(-) delete mode 100644 packages/agent/src/agents/operators/mcp-agent/mcpAgent.ts delete mode 100644 packages/agent/src/agents/operators/mcp-agent/mcpAgentTools.ts rename packages/agent/src/services/mcp/src/{mcp.ts => mcp.service.ts} (100%) diff --git a/packages/agent/src/__mocks__/@snakagent/core.ts b/packages/agent/src/__mocks__/@snakagent/core.ts index 308c0734c..cfbe33122 100644 --- a/packages/agent/src/__mocks__/@snakagent/core.ts +++ b/packages/agent/src/__mocks__/@snakagent/core.ts @@ -1,11 +1,3 @@ -export interface ApiKeys { - openai?: string; - anthropic?: string; - gemini?: string; - deepseek?: string; - [providerName: string]: string | undefined; -} - export const logger = { warn: (..._args: unknown[]): void => { void _args; diff --git a/packages/agent/src/agents/operators/mcp-agent/mcpAgent.ts b/packages/agent/src/agents/operators/mcp-agent/mcpAgent.ts deleted file mode 100644 index b43a577f8..000000000 --- a/packages/agent/src/agents/operators/mcp-agent/mcpAgent.ts +++ /dev/null @@ -1,276 +0,0 @@ -/* *** Depracted *** */ -import { BaseAgent } from '../../core/baseAgent.js'; -import { BaseMessage, AIMessage, HumanMessage } from '@langchain/core/messages'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { AgentConfig, logger } from '@snakagent/core'; -import { OperatorRegistry } from '../operatorRegistry.js'; -import { getMcpAgentTools } from './mcpAgentTools.js'; -import { createReactAgent } from '@langchain/langgraph/prebuilt'; -import { mcpAgentSystemPrompt } from '../../../shared/prompts/core/mcpAgentPrompts.js'; -import { DynamicStructuredTool } from '@langchain/core/tools'; -import { AgentType } from '@enums/agent.enum.js'; - -/** - * Interface defining the configuration options for the MCPAgent - */ -export interface MCPAgentConfig { - debug?: boolean; - modelType?: 'fast' | 'smart' | 'cheap'; -} - -/** - * Enhanced MCP Agent using LangChain Tools for intelligent MCP server and tool management - */ -export class MCPAgent extends BaseAgent { - private debug: boolean = false; - private reactAgent: ReturnType; - private tools: DynamicStructuredTool[]; - - constructor(config: MCPAgentConfig = {}, agentConfig: AgentConfig.Runtime) { - super('mcp-agent', AgentType.OPERATOR, agentConfig); - - this.debug = config.debug !== undefined ? config.debug : true; - this.tools = getMcpAgentTools(); - - if (this.debug) { - logger.debug( - `MCPAgent initialized with ${this.tools.length} tools: ${this.tools.map((t) => t.name).join(', ')}` - ); - } - } - - /** - * Initializes the MCPAgent by setting up the React agent and registering with the operator registry - * @throws {Error} If initialization fails - * @returns {Promise} - */ - public async init(): Promise { - try { - this.reactAgent = createReactAgent({ - llm: this.agentConfig.graph.model, - tools: this.tools, - stateModifier: mcpAgentSystemPrompt(), - }); - - const registry = OperatorRegistry.getInstance(); - registry.register(this.id, this); - - logger.debug( - 'MCPAgent initialized with React agent and registered successfully' - ); - } catch (error) { - logger.error(`MCPAgent initialization failed: ${error}`); - throw new Error(`MCPAgent initialization failed: ${error}`); - } - } - - /** - * Executes MCP management operations using the React agent and tools - * @param {string | BaseMessage | BaseMessage[]} input - The input message(s) to process - * @param {Record} config - Additional configuration options - * @returns {Promise} The agent's response as an AIMessage - * @throws {Error} If execution fails or the agent is not initialized - */ - public async execute( - input: string | BaseMessage | BaseMessage[], - isInterrupted: boolean = false, - config?: Record - ): Promise { - try { - const content = this.extractOriginalUserContent(input, config); - - if (this.debug) { - logger.debug(`MCPAgent: Processing request: "${content}"`); - logger.debug(`MCPAgent: Config received:`, { - originalUserQuery: config?.originalUserQuery, - hasConfig: !!config, - configKeys: config ? Object.keys(config) : [], - }); - } - - if (!this.reactAgent) { - throw new Error('React agent not initialized. Call init() first.'); - } - - const result = await this.reactAgent.invoke({ - messages: [new HumanMessage(content)], - }); - - const messages = result.messages || []; - const lastMessage = messages[messages.length - 1]; - - let responseContent = ''; - if (lastMessage && lastMessage.content) { - responseContent = - typeof lastMessage.content === 'string' - ? lastMessage.content - : JSON.stringify(lastMessage.content); - } else { - responseContent = 'MCP operation completed.'; - } - - return new AIMessage({ - content: responseContent, - additional_kwargs: { - from: 'mcp-agent', - final: true, - success: true, - }, - }); - } catch (error) { - logger.error(`MCPAgent execution error: ${error}`); - - return new AIMessage({ - content: `MCP operation failed: ${error instanceof Error ? error.message : String(error)}`, - additional_kwargs: { - from: 'mcp-agent', - final: true, - success: false, - error: error instanceof Error ? error.message : String(error), - }, - }); - } - } - - /** - * Extracts the original user content from various input sources - * @private - * @param {string | BaseMessage | BaseMessage[]} input - The input to extract content from - * @param {Record} config - Additional configuration containing potential original user query - * @returns {string} The extracted user content - */ - private extractOriginalUserContent( - input: string | BaseMessage | BaseMessage[], - config?: Record - ): string { - if ( - config?.originalUserQuery && - typeof config.originalUserQuery === 'string' - ) { - if (this.debug) { - logger.debug( - `MCPAgent: Using originalUserQuery from config: "${config.originalUserQuery}"` - ); - } - return config.originalUserQuery; - } - - if (Array.isArray(input)) { - for (const message of input) { - if ( - message.additional_kwargs?.originalUserQuery && - typeof message.additional_kwargs.originalUserQuery === 'string' - ) { - if (this.debug) { - logger.debug( - `MCPAgent: Using originalUserQuery from message additional_kwargs` - ); - } - return message.additional_kwargs.originalUserQuery; - } - } - - for (const message of input) { - if ( - message instanceof HumanMessage && - typeof message.content === 'string' - ) { - if (this.debug) { - logger.debug(`MCPAgent: Using first HumanMessage content`); - } - return message.content; - } - } - - const lastMessage = input[input.length - 1]; - const content = - typeof lastMessage.content === 'string' - ? lastMessage.content - : JSON.stringify(lastMessage.content); - - if (this.debug) { - logger.debug(`MCPAgent: Fallback to last message content`); - } - return content; - } - - if (input instanceof BaseMessage) { - if ( - input.additional_kwargs?.originalUserQuery && - typeof input.additional_kwargs.originalUserQuery === 'string' - ) { - if (this.debug) { - logger.debug( - `MCPAgent: Using originalUserQuery from single message additional_kwargs` - ); - } - return input.additional_kwargs.originalUserQuery; - } - - const content = - typeof input.content === 'string' - ? input.content - : JSON.stringify(input.content); - - if (this.debug) { - logger.debug(`MCPAgent: Using single message content`); - } - return content; - } - - if (typeof input === 'string') { - if (this.debug) { - logger.debug(`MCPAgent: Using string input directly`); - } - return input; - } - - if (this.debug) { - logger.debug(`MCPAgent: Using fallback content extraction`); - } - return this.extractContent(input); - } - - /** - * Extracts content from various input types (fallback method) - * @private - * @param {string | BaseMessage | BaseMessage[]} input - The input to extract content from - * @returns {string} The extracted content - */ - private extractContent(input: string | BaseMessage | BaseMessage[]): string { - if (typeof input === 'string') { - return input; - } - if (Array.isArray(input)) { - const lastMessage = input[input.length - 1]; - return typeof lastMessage.content === 'string' - ? lastMessage.content - : JSON.stringify(lastMessage.content); - } - return typeof input.content === 'string' - ? input.content - : JSON.stringify(input.content); - } - - /** - * Returns the list of available tools for the MCP agent - * @returns {any[]} Array of available tools - */ - public getTools() { - return this.tools; - } - - /** - * Cleans up resources and unregisters the agent from the operator registry - * @returns {Promise} - */ - public async dispose(): Promise { - try { - const registry = OperatorRegistry.getInstance(); - registry.unregister(this.id); - logger.debug('MCPAgent disposed and unregistered'); - } catch (error) { - logger.error(`Error disposing MCPAgent: ${error}`); - } - } -} diff --git a/packages/agent/src/agents/operators/mcp-agent/mcpAgentTools.ts b/packages/agent/src/agents/operators/mcp-agent/mcpAgentTools.ts deleted file mode 100644 index bf5a8f98e..000000000 --- a/packages/agent/src/agents/operators/mcp-agent/mcpAgentTools.ts +++ /dev/null @@ -1,627 +0,0 @@ -/* *** Depracted *** */ -import { DynamicStructuredTool } from '@langchain/core/tools'; -import { z } from 'zod'; -import { getGuardValue, logger } from '@snakagent/core'; -import { MCP_CONTROLLER } from '../../../services/mcp/src/mcp.js'; -import { Postgres } from '@snakagent/database'; -import { AgentConfig } from '@snakagent/core'; -import { OperatorRegistry } from '../operatorRegistry.js'; -import { BaseAgent } from '../../core/baseAgent.js'; - -interface AgentWithTools extends BaseAgent { - getTools?: () => DynamicStructuredTool[]; - tools?: DynamicStructuredTool[]; -} - -interface SmitheryServerResponse { - qualifiedName: string; - displayName: string; - description: string; - homepage: string; - useCount: string; - isDeployed: boolean; - createdAt: string; -} - -interface SmitheryListResponse { - servers: SmitheryServerResponse[]; - pagination: { - currentPage: number; - pageSize: number; - totalPages: number; - totalCount: number; - }; -} - -interface SmitheryServerDetail { - qualifiedName: string; - displayName: string; - iconUrl: string | null; - deploymentUrl: string; - connections: Array<{ - type: string; - url?: string; - configSchema: any; - }>; - security: { - scanPassed: boolean; - } | null; - tools: Array<{ - name: string; - description: string | null; - inputSchema: { - type: 'object'; - properties?: object; - }; - }> | null; -} - -/** - * Creates a set of tools for managing MCP servers - * @returns Array of DynamicStructuredTool instances for MCP management - */ -export function getMcpAgentTools(): DynamicStructuredTool[] { - return [ - new DynamicStructuredTool({ - name: 'search_mcp_server', - description: - 'Search for MCP servers on Smithery using a human readable search request', - schema: z.object({ - query: z - .string() - .max(getGuardValue('mcp.max_query_length')) - .describe( - 'Human readable search query for MCP servers (e.g., "web search", "file management", "memory")' - ), - limit: z - .number() - .max(getGuardValue('mcp.max_limit_tools')) - .optional() - .describe('Maximum number of results to return (default: 10)'), - deployedOnly: z - .boolean() - .optional() - .describe('Only return deployed servers (default: false)'), - verifiedOnly: z - .boolean() - .optional() - .describe('Only return verified servers (default: false)'), - }), - func: async ({ - query, - limit = 10, - deployedOnly = false, - verifiedOnly = false, - }) => { - try { - const apiKey = process.env.SMITHERY_API_KEY; - if (!apiKey) { - throw new Error( - 'SMITHERY_API_KEY environment variable is required' - ); - } - - let searchQuery = query; - if (deployedOnly) searchQuery += ' is:deployed'; - if (verifiedOnly) searchQuery += ' is:verified'; - - const searchParams = new URLSearchParams({ - q: searchQuery, - page: '1', - pageSize: limit.toString(), - }); - - const response = await fetch( - `https://registry.smithery.ai/servers?${searchParams.toString()}`, - { - headers: { - Authorization: `Bearer ${apiKey}`, - Accept: 'application/json', - }, - } - ); - - if (!response.ok) { - if (response.status === 401) { - throw new Error( - 'Invalid Smithery API key. Please check your SMITHERY_API_KEY environment variable.' - ); - } - throw new Error( - `Smithery API request failed: ${response.status} ${response.statusText}` - ); - } - - if (response.bodyUsed) { - throw new Error( - 'Response body already consumed in main search request' - ); - } - const searchResult: SmitheryListResponse = await response.json(); - - if (!searchResult.servers || searchResult.servers.length === 0) { - return JSON.stringify( - { - success: true, - message: 'No MCP servers found matching your query', - query: query, - servers: [], - totalCount: 0, - }, - null, - 2 - ); - } - - const serverDetails = await Promise.all( - searchResult.servers.map(async (server) => { - try { - const detailResponse = await fetch( - `https://registry.smithery.ai/servers/${encodeURIComponent(server.qualifiedName)}`, - { - headers: { - Authorization: `Bearer ${apiKey}`, - Accept: 'application/json', - }, - } - ); - - if (!detailResponse.ok) { - logger.warn( - `Failed to get details for server ${server.qualifiedName}: ${detailResponse.status}` - ); - return { - ...server, - connections: [], - tools: [], - configSchema: null, - }; - } - - if (detailResponse.bodyUsed) { - throw new Error( - `Response body already consumed for server ${server.qualifiedName}` - ); - } - const detail: SmitheryServerDetail = - await detailResponse.json(); - - const httpConnection = detail.connections.find( - (conn) => conn.type === 'http' - ); - const stdioConnection = detail.connections.find( - (conn) => conn.type === 'stdio' - ); - - return { - qualifiedName: server.qualifiedName, - displayName: server.displayName, - description: server.description, - homepage: server.homepage, - useCount: server.useCount, - isDeployed: server.isDeployed, - isVerified: detail.security?.scanPassed || false, - tools: detail.tools || [], - toolCount: detail.tools?.length || 0, - connections: detail.connections.map((conn) => ({ - type: conn.type, - url: conn.url, - hasConfig: !!( - conn.configSchema?.properties && - Object.keys(conn.configSchema.properties).length > 0 - ), - requiredFields: conn.configSchema?.required || [], - configFields: conn.configSchema?.properties - ? Object.keys(conn.configSchema.properties) - : [], - })), - installation: { - isRemote: server.isDeployed && httpConnection, - requiresApiKey: server.isDeployed, - hasLocalOption: !!stdioConnection, - configurationRequired: !!( - httpConnection?.configSchema?.required?.length || - stdioConnection?.configSchema?.required?.length - ), - }, - }; - } catch (error) { - logger.error( - `Error getting details for server ${server.qualifiedName}: ${error}` - ); - return { - ...server, - connections: [], - tools: [], - installation: { - isRemote: false, - requiresApiKey: false, - hasLocalOption: false, - configurationRequired: false, - }, - }; - } - }) - ); - - return JSON.stringify( - { - success: true, - message: `Found ${searchResult.servers.length} matching MCP servers`, - query: query, - totalCount: searchResult.pagination.totalCount, - currentPage: searchResult.pagination.currentPage, - totalPages: searchResult.pagination.totalPages, - servers: serverDetails, - usage: { - tip: "Use 'install_mcp_server' to install any of these servers for an agent", - note: 'Remote servers require a Smithery API key, local servers can run without one', - configHelp: - "Check 'installation.configurationRequired' to see if additional configuration is needed", - }, - }, - null, - 2 - ); - } catch (error) { - logger.error(`Error searching MCP servers: ${error}`); - throw new Error(`Failed to search MCP servers: ${error}`); - } - }, - }), - - new DynamicStructuredTool({ - name: 'install_mcp_server', - description: - 'Install an MCP server configuration for an agent using Smithery qualified name', - schema: z.object({ - agentId: z - .string() - .uuid('Invalid agentId format') - .describe('The ID of the agent to install the MCP server for'), - qualifiedName: z - .string() - .trim() - .max(getGuardValue('mcp.max_qualified_name_length')) - .describe( - 'The Smithery qualified name of the MCP server (from search results)' - ), - serverName: z - .string() - .trim() - .max(getGuardValue('mcp.max_server_name_length')) - .optional() - .describe('Custom name for the server (defaults to qualified name)'), - config: z - .record(z.any()) - .refine( - (val) => - JSON.stringify(val).length <= - getGuardValue('mcp.max_config_size'), - { - message: 'Configuration object exceeds maximum size limit', - } - ) - .optional() - .describe('Configuration values required by the server'), - profile: z - .string() - .max(getGuardValue('mcp.max_profile_length')) - .optional() - .describe('Smithery profile to use (if available)'), - }), - func: async ({ - agentId, - qualifiedName, - serverName, - config = {}, - profile, - }) => { - try { - // PostgresQuery relation : agents - const findQuery = new Postgres.Query( - 'SELECT * FROM agents WHERE id = $1', - [agentId] - ); - const existingAgent = - await Postgres.query(findQuery); - - if (existingAgent.length === 0) { - throw new Error(`Agent not found: ${agentId}`); - } - - const agent = existingAgent[0]; - const currentMcpServers = agent.mcp_servers || {}; - - const finalServerName = - serverName || qualifiedName.split('/').pop() || qualifiedName; - - if (currentMcpServers[finalServerName]) { - throw new Error( - `MCP server "${finalServerName}" already exists for agent "${agentId}"` - ); - } - - interface SmitheryCliConfig { - command: string; - args: string[]; - } - - const smitheryConfig: SmitheryCliConfig = { - command: 'npx', - args: ['-y', '@smithery/cli@latest', 'run', qualifiedName], - }; - - const smitheryApiKey = process.env.SMITHERY_API_KEY; - if (smitheryApiKey) { - smitheryConfig.args.push('--key', smitheryApiKey); - } - - if (profile) { - smitheryConfig.args.push('--profile', profile); - } - - if (config && Object.keys(config).length > 0) { - const encodedConfig = JSON.stringify(JSON.stringify(config)); - smitheryConfig.args.push('--config', encodedConfig); - } - - currentMcpServers[finalServerName] = smitheryConfig; - - const updateQuery = new Postgres.Query( - 'UPDATE agents SET "mcp_servers" = $1 WHERE id = $2 RETURNING *', - [currentMcpServers, agentId] - ); - - const result = - await Postgres.query(updateQuery); - - return JSON.stringify( - { - success: true, - message: `MCP server "${finalServerName}" (${qualifiedName}) installed for agent "${agentId}"`, - serverName: finalServerName, - qualifiedName: qualifiedName, - configuration: smitheryConfig, - nextSteps: [ - 'Use "refresh_mcp_server" to restart the agent with the new MCP server', - 'The server will be available after the agent restart', - ], - data: result[0], - }, - null, - 2 - ); - } catch (error) { - logger.error(`Error installing MCP server: ${error}`); - throw new Error(`Failed to install MCP server: ${error}`); - } - }, - }), - - new DynamicStructuredTool({ - name: 'list_mcp_servers', - description: 'List all MCP servers configured for a specific agent', - schema: z.object({ - agentId: z - .string() - .uuid('Invalid agentId format') - .describe('The ID of the agent to list MCP servers for'), - }), - func: async ({ agentId }) => { - try { - const query = new Postgres.Query( - 'SELECT id, name, "mcp_servers" FROM agents WHERE id = $1', - [agentId] - ); - const result = await Postgres.query(query); - - if (result.length === 0) { - throw new Error(`Agent not found: ${agentId}`); - } - - const agent = result[0]; - return JSON.stringify( - { - success: true, - agentId: agent.id, - agentName: agent.profile.name, - mcp_servers: agent.mcp_servers || {}, - }, - null, - 2 - ); - } catch (error) { - logger.error(`Error listing MCP servers: ${error}`); - throw new Error(`Failed to list MCP servers: ${error}`); - } - }, - }), - - new DynamicStructuredTool({ - name: 'refresh_mcp_server', - description: 'Restart an agent with its MCP servers', - schema: z.object({ - agentId: z - .string() - .uuid('Invalid agentId format') - .describe('The ID of the agent to refresh'), - timeout: z - .number() - .max(getGuardValue('mcp.max_timeout')) - .optional() - .describe( - 'Timeout in milliseconds for MCP initialization (default: 30000)' - ), - }), - func: async ({ agentId, timeout = 30000 }) => { - try { - logger.info(`Starting MCP server refresh for agent ${agentId}`); - - const query = new Postgres.Query( - 'SELECT "mcp_servers" FROM agents WHERE id = $1', - [agentId] - ); - const result = await Postgres.query(query); - - if (result.length === 0) { - throw new Error(`Agent not found: ${agentId}`); - } - - const mcp_servers = result[0].mcp_servers || {}; - - if (!mcp_servers || Object.keys(mcp_servers).length === 0) { - logger.info(`No MCP servers configured for agent ${agentId}`); - return JSON.stringify({ - success: true, - message: `No MCP servers configured for agent ${agentId}`, - mcpToolsCount: 0, - }); - } - - logger.info( - `Found ${Object.keys(mcp_servers).length} MCP servers configured for agent ${agentId}` - ); - - const initializeMcpWithTimeout = async () => { - return Promise.race([ - (async () => { - logger.info('Creating new MCP controller...'); - const mcp = new MCP_CONTROLLER(mcp_servers); - - logger.info('Initializing MCP connections...'); - await mcp.initializeConnections(); - - logger.info('Getting MCP tools...'); - const mcpTools = mcp.getTools() as DynamicStructuredTool[]; - - logger.info(`Retrieved ${mcpTools.length} MCP tools`); - return mcpTools; - })(), - new Promise((_, reject) => { - setTimeout(() => { - reject( - new Error(`MCP initialization timed out after ${timeout}ms`) - ); - }, timeout); - }), - ]); - }; - - const mcpTools = await initializeMcpWithTimeout(); - - try { - const registry = OperatorRegistry.getInstance(); - const mcpAgent = registry.getAgent('mcp-agent') as AgentWithTools; - - if (mcpAgent) { - logger.info('Updating MCP agent tools...'); - if (mcpAgent.getTools) { - const currentTools = mcpAgent - .getTools() - .filter( - (tool: DynamicStructuredTool) => - !tool.name.startsWith('mcp_') - ); - mcpAgent.tools = [...currentTools, ...mcpTools]; - logger.info( - `Updated MCP agent with ${mcpTools.length} new tools` - ); - } - } else { - logger.warn( - 'MCP Agent not found in registry - tools not updated' - ); - } - } catch (registryError) { - logger.warn(`Failed to update agent registry: ${registryError}`); - } - - return JSON.stringify({ - success: true, - message: `Successfully refreshed MCP servers for agent ${agentId}`, - mcpToolsCount: mcpTools.length, - serversRefreshed: Object.keys(mcp_servers), - timeoutUsed: timeout, - }); - } catch (error) { - logger.error( - `Error refreshing MCP server for agent ${agentId}: ${error}` - ); - - if (error instanceof Error) { - if (error.message.includes('timed out')) { - throw new Error( - `MCP server refresh timed out after ${timeout}ms. Try increasing the timeout or check if MCP servers are responding.` - ); - } else if ( - error.message.includes('ECONNREFUSED') || - error.message.includes('connection') - ) { - throw new Error( - `Failed to connect to MCP servers. Check if the servers are running and accessible.` - ); - } else { - throw new Error( - `Failed to refresh MCP servers: ${error.message}` - ); - } - } else { - throw new Error(`Failed to refresh MCP servers: ${error}`); - } - } - }, - }), - - new DynamicStructuredTool({ - name: 'delete_mcp_server', - description: 'Delete an MCP server configuration', - schema: z.object({ - agentId: z.string().describe('The ID of the agent'), - serverName: z.string().describe('The name of the MCP server to delete'), - }), - func: async ({ agentId, serverName }) => { - try { - const findQuery = new Postgres.Query( - 'SELECT * FROM agents WHERE id = $1', - [agentId] - ); - const existingAgent = - await Postgres.query(findQuery); - - if (existingAgent.length === 0) { - throw new Error(`Agent not found: ${agentId}`); - } - - const agent = existingAgent[0]; - const currentMcpServers = agent.mcp_servers || {}; - - if (!currentMcpServers[serverName]) { - throw new Error( - `MCP server "${serverName}" not found in agent "${agentId}"` - ); - } - - delete currentMcpServers[serverName]; - - const updateQuery = new Postgres.Query( - 'UPDATE agents SET "mcp_servers" = $1 WHERE id = $2 RETURNING *', - [currentMcpServers, agentId] - ); - - const result = - await Postgres.query(updateQuery); - - return JSON.stringify({ - success: true, - message: `MCP server "${serverName}" deleted from agent "${agentId}"`, - data: result[0], - }); - } catch (error) { - logger.error(`Error deleting MCP server: ${error}`); - throw new Error(`Failed to delete MCP server: ${error}`); - } - }, - }), - ]; -} diff --git a/packages/agent/src/services/mcp/src/__tests__/mcp.spec.ts b/packages/agent/src/services/mcp/src/__tests__/mcp.spec.ts index 89e7c952e..556b8c320 100644 --- a/packages/agent/src/services/mcp/src/__tests__/mcp.spec.ts +++ b/packages/agent/src/services/mcp/src/__tests__/mcp.spec.ts @@ -29,7 +29,7 @@ jest.mock('@snakagent/core', () => ({ }, })); -import { MCP_CONTROLLER } from '../mcp.js'; +import { MCP_CONTROLLER } from '../mcp.service.js'; import { StructuredTool } from '@langchain/core/tools'; import { MultiServerMCPClient } from 'snak-mcps'; import { logger, AgentConfig, AgentMode } from '@snakagent/core'; diff --git a/packages/agent/src/services/mcp/src/mcp.ts b/packages/agent/src/services/mcp/src/mcp.service.ts similarity index 100% rename from packages/agent/src/services/mcp/src/mcp.ts rename to packages/agent/src/services/mcp/src/mcp.service.ts diff --git a/packages/agent/src/tools/tools.ts b/packages/agent/src/tools/tools.ts index f569ee79f..04d600fde 100644 --- a/packages/agent/src/tools/tools.ts +++ b/packages/agent/src/tools/tools.ts @@ -5,7 +5,7 @@ import { } from '@langchain/core/tools'; import { logger, AgentConfig, supervisorAgentConfig } from '@snakagent/core'; import { AnyZodObject } from 'zod'; -import { MCP_CONTROLLER } from '@services/mcp/src/mcp.js'; +import { MCP_CONTROLLER } from '@services/mcp/src/mcp.service.js'; import { CoreToolRegistry } from '@agents/graphs/tools/core.tools.js'; import { getSupervisorConfigTools } from '@agents/operators/supervisor/supervisorTools.js'; diff --git a/packages/server/config/configuration.ts b/packages/server/config/configuration.ts index e3d6e69a3..7fe1fce28 100644 --- a/packages/server/config/configuration.ts +++ b/packages/server/config/configuration.ts @@ -3,14 +3,12 @@ import { ConfigService } from '@nestjs/config'; import { RpcProvider } from 'starknet'; import { envSchema, type EnvConfig } from './env.validation.js'; import { RagConfigSize } from '@snakagent/core'; // Assuming core exports these types -import { readFileSync } from 'fs'; @Injectable() export class ConfigurationService { private readonly logger = new Logger(ConfigurationService.name); private readonly config: EnvConfig; private readonly ragConfig: RagConfigSize; - private readonly ragConfigPath: string; constructor(private configService: ConfigService) { // Collect all env variables specified in the schema @@ -29,10 +27,7 @@ export class ConfigurationService { AI_MODELS_CONFIG_PATH: this.configService.get( 'AI_MODELS_CONFIG_PATH' ), - OPENAI_API_KEY: this.configService.get('OPENAI_API_KEY'), - ANTHROPIC_API_KEY: this.configService.get('ANTHROPIC_API_KEY'), GEMINI_API_KEY: this.configService.get('GEMINI_API_KEY'), - DEEPSEEK_API_KEY: this.configService.get('DEEPSEEK_API_KEY'), GUARDS_CONFIG_PATH: this.configService.get('GUARDS_CONFIG_PATH'), REDIS_HOST: this.configService.get('REDIS_HOST'), REDIS_PORT: this.configService.get('REDIS_PORT'), @@ -44,11 +39,23 @@ export class ConfigurationService { const result = envSchema.safeParse(envVariables); if (!result.success) { - this.logger.error( - ' Invalid environment variables:', - JSON.stringify(result.error.format(), null, 2) - ); - throw new Error('Invalid environment variables'); + // Format validation errors in a user-friendly way + const errors = result.error.format() as any; + const errorMessages: string[] = []; + + Object.keys(errors).forEach((key) => { + if (key !== '_errors' && errors[key]?._errors?.length > 0) { + const errorList = errors[key]._errors.join(', '); + errorMessages.push(` - ${key}: ${errorList}`); + } + }); + + const formattedError = errorMessages.length > 0 + ? `\n\nMissing or invalid environment variables:\n${errorMessages.join('\n')}\n\nPlease check your .env file and ensure all required variables are set.\n` + : JSON.stringify(errors, null, 2); + + this.logger.error(formattedError); + throw new Error('Invalid environment variables. Check logs above for details.'); } this.config = result.data; @@ -117,4 +124,8 @@ export class ConfigurationService { db, }; } + + get geminiApiKey(): string { + return this.config.GEMINI_API_KEY; + } } diff --git a/packages/server/config/env.validation.ts b/packages/server/config/env.validation.ts index 2ea7e303a..2b68e6f94 100644 --- a/packages/server/config/env.validation.ts +++ b/packages/server/config/env.validation.ts @@ -17,11 +17,8 @@ export const envSchema = z.object({ .optional() .default('config/models/default.models.json'), - // Provider-specific API Keys (optional) - OPENAI_API_KEY: z.string().optional(), - ANTHROPIC_API_KEY: z.string().optional(), - GEMINI_API_KEY: z.string().optional(), - DEEPSEEK_API_KEY: z.string().optional(), + // Provider-specific API Keys + GEMINI_API_KEY: z.string(), // Rag max size configuration RAG_CONFIG_PATH: z.string().optional().default('config/rag/default.rag.json'), diff --git a/packages/server/src/utils/agents.utils.ts b/packages/server/src/utils/agents.utils.ts index 9f1e35d79..c14ec1696 100644 --- a/packages/server/src/utils/agents.utils.ts +++ b/packages/server/src/utils/agents.utils.ts @@ -1,12 +1,12 @@ -import { ChatAnthropic } from '@langchain/anthropic'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; -import { ChatOpenAI } from '@langchain/openai'; import { ModelConfig } from '@snakagent/core'; import { logger } from 'starknet'; +const SUPPORTED_GEMINI_MODELS = ['gemini-2.5-flash', 'gemini-2.5-pro']; + /** - * Initializes model instances based on the loaded configuration. + * Initializes Gemini model instances based on the loaded configuration. * @returns {BaseChatModel | null} Model instance or null if initialization fails. */ export function initializeModels(model: ModelConfig): BaseChatModel | null { @@ -17,37 +17,26 @@ export function initializeModels(model: ModelConfig): BaseChatModel | null { if (!model.provider) { throw new Error('Model provider is not defined'); } - let modelInstance: BaseChatModel | null = null; - const commonConfig = { - modelName: model.model_name, + + // Only support Gemini provider + if (model.provider.toLowerCase() !== 'gemini') { + throw new Error(`Unsupported provider: ${model.provider}. Only 'gemini' is supported.`); + } + + // Validate model name + if (!SUPPORTED_GEMINI_MODELS.includes(model.model_name)) { + throw new Error( + `Unsupported Gemini model: ${model.model_name}. Supported models: ${SUPPORTED_GEMINI_MODELS.join(', ')}` + ); + } + + const modelInstance = new ChatGoogleGenerativeAI({ + model: model.model_name, verbose: false, temperature: model.temperature, - }; - switch (model.provider.toLowerCase()) { - case 'openai': - modelInstance = new ChatOpenAI({ - ...commonConfig, - openAIApiKey: process.env.OPENAI_API_KEY, - }); - break; - case 'anthropic': - modelInstance = new ChatAnthropic({ - ...commonConfig, - anthropicApiKey: process.env.ANTHROPIC_API_KEY, - }); - break; - case 'gemini': - modelInstance = new ChatGoogleGenerativeAI({ - model: model.model_name, // Updated to valid Gemini model name - verbose: false, - temperature: model.temperature, - apiKey: process.env.GEMINI_API_KEY, - }); - break; - // Add case for 'deepseek' if a Langchain integration exists or becomes available - default: - throw new Error('No valid model provided'); - } + apiKey: process.env.GEMINI_API_KEY, + }); + return modelInstance; } catch (error) { logger.error( From 7262c7be8a0b61b2eeb044349e6e77601a206ca6 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 13:12:07 +0000 Subject: [PATCH 05/18] version without starknet --- .env.example | 56 ++-- config/guards/default.guards.json | 6 +- package.json | 2 - packages/agent/src/agents/core/snakAgent.ts | 19 +- .../tools/schemas/common.schemas.ts | 10 +- .../agent/src/agents/studio/studio-graph.ts | 241 +++++++++--------- .../agent/interfaces/agent.interface.ts | 6 - .../core/src/config/guards/guardsSchema.ts | 6 +- .../src/services/agent-validation.service.ts | 32 +-- packages/server/config/configuration.ts | 26 +- packages/server/config/env.validation.ts | 10 +- packages/server/src/agents.storage.ts | 8 +- packages/workers/package.json | 1 - pnpm-lock.yaml | 117 --------- tests/src/file-test/file-upload.ts | 2 +- 15 files changed, 175 insertions(+), 367 deletions(-) diff --git a/.env.example b/.env.example index 7fbd6f9e7..64d35a1e7 100644 --- a/.env.example +++ b/.env.example @@ -1,51 +1,33 @@ -# --- Starknet configuration (mandatory) --- -STARKNET_PUBLIC_ADDRESS="YOUR_STARKNET_PUBLIC_ADDRESS" -STARKNET_PRIVATE_KEY="YOUR_STARKNET_PRIVATE_KEY" -STARKNET_RPC_URL="YOUR_STARKNET_RPC_URL" - -# --- AI Model API Keys (mandatory) --- -# Add the API keys for the specific AI providers you use in config/models/default.models.json -# The agent will automatically load the correct key based on the provider name. - -# Example for OpenAI: -OPENAI_API_KEY="YOUR_OPENAI_API_KEY" # (e.g., sk-...) - -# Example for Anthropic: -ANTHROPIC_API_KEY="YOUR_ANTHROPIC_API_KEY" # (e.g., sk-ant-...) - -# Example for Google Gemini: -GEMINI_API_KEY="YOUR_GEMINI_API_KEY" - -# Example for DeepSeek: -DEEPSEEK_API_KEY="YOUR_DEEPSEEK_API_KEY" - -# Note: You do not need an API key if using a local Ollama model. - # --- General Agent Configuration (mandatory) --- -SERVER_API_KEY="YOUR_SERVER_API_KEY" # A secret key for your agent server API -SERVER_PORT="3001" +SERVER_API_KEY="YOUR_SERVER_API_KEY" +SERVER_PORT="3002" # --- PostgreSQL Database Configuration (mandatory) --- POSTGRES_USER=admin -POSTGRES_HOST=localhost # dev -# POSTGRES_HOST=postgres # prod +POSTGRES_HOST=localhost POSTGRES_DB=postgres POSTGRES_PASSWORD=admin POSTGRES_PORT=5432 # --- LangSmith Tracing (Optional) --- -# Set LANGSMITH_TRACING=true to enable tracing LANGSMITH_TRACING=false -LANGSMITH_ENDPOINT="https://api.smith.langchain.com" -LANGSMITH_API_KEY="YOUR_LANGSMITH_API_KEY" # (Only needed if LANGSMITH_TRACING=true) -LANGSMITH_PROJECT="Snak" # (Optional project name for LangSmith) +LANGSMITH_ENDPOINT="https://eu.api.smith.langchain.com/" +LANGSMITH_API_KEY="YOUR_LANGSMITH_API_KEY" +LANGSMITH_PROJECT="Snak" +# --- AI Model Configuration (mandatory) --- +GEMINI_API_KEY="YOUR_GEMINI_API_KEY" +# --- Redis Configuration (mandatory) --- +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD="YOUR_REDIS_PASSWORD" +REDIS_DB=0 -DEFAULT_MODEL_PROVIDER="your_default_provider" # e.g., "openai", "anthropic", "gemini", "deepseek", "ollama" -DEFAULT_MODEL_NAME="your_default_model_name" # e.g., "gpt-4o", "claude-3", "gemini-1.5", "deepseek-v1", "llama2" -DEFAULT_TEMPERATURE="your_default_temperature" # e.g., "0.7" -DEFAULT_MAX_TOKENS="your_default_max_tokens" # e.g., "4096" +# --- Snak User ID (mandatory) --- +SNAK_USER_ID="YOUR_SNAK_USER_ID" -# --- Node Environment --- -NODE_ENV="development" # "development" or "production" \ No newline at end of file +# --- Default Model Configuration (mandatory) --- +DEFAULT_MODEL_PROVIDER="gemini" +DEFAULT_MODEL_NAME="gemini-2.5-flash" +DEFAULT_TEMPERATURE=0.7 \ No newline at end of file diff --git a/config/guards/default.guards.json b/config/guards/default.guards.json index 6968d0692..d46fb9899 100644 --- a/config/guards/default.guards.json +++ b/config/guards/default.guards.json @@ -93,10 +93,8 @@ "max_execution_timeout_ms": 500000, "max_token_usage": 200000, "model": { - "provider_max_length": 100, - "provider_min_length": 3, - "model_name_max_length": 100, - "model_name_min_length": 3, + "allowed_provider": "gemini", + "allowed_models": ["gemini-2.5-flash", "gemini-2.5-pro"], "max_temperature": 1, "max_tokens": 8192 } diff --git a/package.json b/package.json index 1c9e0a743..4625db46c 100755 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "private": true, "packageManager": "pnpm@9.3.0", "keywords": [ - "starknet", "blockchain", "AI", "agent", @@ -77,7 +76,6 @@ "pg": "^8.15.6", "prom-client": "^15.1.3", "socket.io": "^4.8.1", - "starknet": "^6.24.1", "util": "^0.12.5", "uuid": "^11.1.0", "zod": "^3.25.76" diff --git a/packages/agent/src/agents/core/snakAgent.ts b/packages/agent/src/agents/core/snakAgent.ts index a9c948c7f..6633582d6 100644 --- a/packages/agent/src/agents/core/snakAgent.ts +++ b/packages/agent/src/agents/core/snakAgent.ts @@ -1,6 +1,6 @@ import { BaseAgent } from './baseAgent.js'; import { RpcProvider } from 'starknet'; -import { logger, AgentConfig, StarknetConfig } from '@snakagent/core'; +import { logger, AgentConfig } from '@snakagent/core'; import { BaseMessage, HumanMessage } from '@langchain/core/messages'; import { AgentType } from '../../shared/enums/agent.enum.js'; import { createGraph } from '../graphs/core-graph/agent.graph.js'; @@ -32,14 +32,9 @@ import { * Supports multiple execution modes: interactive, autonomous, and hybrid */ export class SnakAgent extends BaseAgent { - private readonly provider: RpcProvider; private ragAgent: RagAgent | null = null; - constructor( - starknet_config: StarknetConfig, - agent_config: AgentConfig.Runtime - ) { + constructor(agent_config: AgentConfig.Runtime) { super('snak', AgentType.SNAK, agent_config); - this.provider = starknet_config.provider; } /** * Initialize the SnakAgent and create the appropriate executor @@ -121,15 +116,7 @@ export class SnakAgent extends BaseAgent { } return this.ragAgent; } - - /** - * Get Starknet RPC provider - * @returns The RpcProvider instance - */ - public getProvider(): RpcProvider { - return this.provider; - } - + public async dispose(): Promise { this.stop(); if (this.pgCheckpointer) { diff --git a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts index 3746796b6..155680c30 100644 --- a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts +++ b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts @@ -38,14 +38,16 @@ const modelGuardsValues: GuardsConfig['agents']['graph']['model'] = export const ModelConfigSchema = z.object({ provider: z .string() - .min(modelGuardsValues.provider_min_length) - .max(modelGuardsValues.provider_max_length) + .refine((val) => val.toLowerCase() === modelGuardsValues.allowed_provider.toLowerCase(), { + message: `Provider must be '${modelGuardsValues.allowed_provider}'`, + }) .optional() .describe('Model provider'), model_name: z .string() - .min(modelGuardsValues.model_name_min_length) - .max(modelGuardsValues.model_name_max_length) + .refine((val) => modelGuardsValues.allowed_models.includes(val), { + message: `Model must be one of: ${modelGuardsValues.allowed_models.join(', ')}`, + }) .optional() .describe('Model name'), temperature: z diff --git a/packages/agent/src/agents/studio/studio-graph.ts b/packages/agent/src/agents/studio/studio-graph.ts index a5145361f..a64834243 100644 --- a/packages/agent/src/agents/studio/studio-graph.ts +++ b/packages/agent/src/agents/studio/studio-graph.ts @@ -1,126 +1,121 @@ -/** File deprecated **/ -import { Graph } from '@agents/graphs/core-graph/agent.graph.js'; -import { BaseChatModel } from '@langchain/core/language_models/chat_models'; -import { AgentConfig, StarknetConfig } from '@snakagent/core'; -import { Postgres } from '@snakagent/database'; -import { ChatOpenAI } from '@langchain/openai'; -import { ChatAnthropic } from '@langchain/anthropic'; -import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; -import { SnakAgent } from '@agents/core/snakAgent.js'; -import { - TASK_EXECUTOR_SYSTEM_PROMPT, - TASK_MANAGER_SYSTEM_PROMPT, - TASK_MEMORY_MANAGER_SYSTEM_PROMPT, - TASK_VERIFIER_SYSTEM_PROMPT, -} from '@prompts/index.js'; -async function getAgentConfigFromId(agentId: string) { - await Postgres.connect({ - database: process.env.POSTGRES_DB as string, - host: process.env.POSTGRES_HOST as string, - user: process.env.POSTGRES_USER as string, - password: process.env.POSTGRES_PASSWORD as string, - port: parseInt(process.env.POSTGRES_PORT as string), - }); - const query = new Postgres.Query( - ` - SELECT - id, - user_id, - row_to_json(profile) as profile, - mcp_servers as "mcp_servers", - prompts_id, - row_to_json(graph) as graph, - row_to_json(memory) as memory, - row_to_json(rag) as rag, - FROM agents - WHERE id = $1 - `, - [agentId] - ); - const result = await Postgres.query(query); - if (result.length === 0) { - throw new Error(`Agent with ID ${agentId} not found`); - } - return result[0]; -} -/** - * Create an autonomous agent graph - * @param agentId - The unique identifier of the agent - * @returns Promise - The initialized graph - */ -export async function createAutonomousAgent(agentId: string): Promise { - const agent_config = await getAgentConfigFromId(agentId); - if (!agent_config) { - throw new Error(`Agent with ID ${agentId} not found`); - } - let model = process.env.DEFAULT_MODEL_PROVIDER; - if (!model) { - throw new Error('Model configuration is not defined'); - } - let modelInstance: BaseChatModel | null = null; - const commonConfig = { - modelName: process.env.DEFAULT_MODEL_NAME as string, - verbose: false, - temperature: parseFloat(process.env.DEFAULT_TEMPERATURE ?? '0.7'), - }; - switch (model.toLowerCase()) { - case 'openai': - modelInstance = new ChatOpenAI({ - ...commonConfig, - openAIApiKey: process.env.OPENAI_API_KEY, - }); - break; - case 'anthropic': - modelInstance = new ChatAnthropic({ - ...commonConfig, - anthropicApiKey: process.env.ANTHROPIC_API_KEY, - }); - break; - case 'gemini': - modelInstance = new ChatGoogleGenerativeAI({ - model: commonConfig.modelName, // Updated to valid Gemini model name - verbose: commonConfig.verbose, - temperature: commonConfig.temperature, - apiKey: process.env.GEMINI_API_KEY, - }); - break; - // Add case for 'deepseek' if a Langchain integration exists or becomes available - default: - throw new Error('No valid model provided'); - } +// /** File deprecated **/ +// import { Graph } from '@agents/graphs/core-graph/agent.graph.js'; +// import { BaseChatModel } from '@langchain/core/language_models/chat_models'; +// import { AgentConfig } from '@snakagent/core'; +// import { Postgres } from '@snakagent/database'; +// import { ChatOpenAI } from '@langchain/openai'; +// import { ChatAnthropic } from '@langchain/anthropic'; +// import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; +// import { SnakAgent } from '@agents/core/snakAgent.js'; +// import { +// TASK_EXECUTOR_SYSTEM_PROMPT, +// TASK_MANAGER_SYSTEM_PROMPT, +// TASK_MEMORY_MANAGER_SYSTEM_PROMPT, +// TASK_VERIFIER_SYSTEM_PROMPT, +// } from '@prompts/index.js'; +// async function getAgentConfigFromId(agentId: string) { +// await Postgres.connect({ +// database: process.env.POSTGRES_DB as string, +// host: process.env.POSTGRES_HOST as string, +// user: process.env.POSTGRES_USER as string, +// password: process.env.POSTGRES_PASSWORD as string, +// port: parseInt(process.env.POSTGRES_PORT as string), +// }); +// const query = new Postgres.Query( +// ` +// SELECT +// id, +// user_id, +// row_to_json(profile) as profile, +// mcp_servers as "mcp_servers", +// prompts_id, +// row_to_json(graph) as graph, +// row_to_json(memory) as memory, +// row_to_json(rag) as rag, +// FROM agents +// WHERE id = $1 +// `, +// [agentId] +// ); +// const result = await Postgres.query(query); +// if (result.length === 0) { +// throw new Error(`Agent with ID ${agentId} not found`); +// } +// return result[0]; +// } +// /** +// * Create an autonomous agent graph +// * @param agentId - The unique identifier of the agent +// * @returns Promise - The initialized graph +// */ +// export async function createAutonomousAgent(agentId: string): Promise { +// const agent_config = await getAgentConfigFromId(agentId); +// if (!agent_config) { +// throw new Error(`Agent with ID ${agentId} not found`); +// } +// let model = process.env.DEFAULT_MODEL_PROVIDER; +// if (!model) { +// throw new Error('Model configuration is not defined'); +// } +// let modelInstance: BaseChatModel | null = null; +// const commonConfig = { +// modelName: process.env.DEFAULT_MODEL_NAME as string, +// verbose: false, +// temperature: parseFloat(process.env.DEFAULT_TEMPERATURE ?? '0.7'), +// }; +// switch (model.toLowerCase()) { +// case 'openai': +// modelInstance = new ChatOpenAI({ +// ...commonConfig, +// openAIApiKey: process.env.OPENAI_API_KEY, +// }); +// break; +// case 'anthropic': +// modelInstance = new ChatAnthropic({ +// ...commonConfig, +// anthropicApiKey: process.env.ANTHROPIC_API_KEY, +// }); +// break; +// case 'gemini': +// modelInstance = new ChatGoogleGenerativeAI({ +// model: commonConfig.modelName, // Updated to valid Gemini model name +// verbose: commonConfig.verbose, +// temperature: commonConfig.temperature, +// apiKey: process.env.GEMINI_API_KEY, +// }); +// break; +// // Add case for 'deepseek' if a Langchain integration exists or becomes available +// default: +// throw new Error('No valid model provided'); +// } - const agent: AgentConfig.Runtime = { - ...agent_config, - prompts: { - task_executor_prompt: TASK_EXECUTOR_SYSTEM_PROMPT, - task_manager_prompt: TASK_MANAGER_SYSTEM_PROMPT, - task_memory_manager_prompt: TASK_MEMORY_MANAGER_SYSTEM_PROMPT, - task_verifier_prompt: TASK_VERIFIER_SYSTEM_PROMPT, - }, - graph: { - ...agent_config.graph, - model: modelInstance, - }, - }; - const starknetConfig: StarknetConfig = { - provider: this.config.starknet.provider, - accountPrivateKey: this.config.starknet.privateKey, - accountPublicKey: this.config.starknet.publicKey, - }; - const snakAgent: SnakAgent = new SnakAgent(starknetConfig, agent, { - database: process.env.POSTGRES_DB as string, - host: process.env.POSTGRES_HOST as string, - user: process.env.POSTGRES_USER as string, - password: process.env.POSTGRES_PASSWORD as string, - port: parseInt(process.env.POSTGRES_PORT as string), - }); - const autonomousAgent = new Graph(snakAgent); - const app = await autonomousAgent.initialize(); - return app; -} +// const agent: AgentConfig.Runtime = { +// ...agent_config, +// prompts: { +// task_executor_prompt: TASK_EXECUTOR_SYSTEM_PROMPT, +// task_manager_prompt: TASK_MANAGER_SYSTEM_PROMPT, +// task_memory_manager_prompt: TASK_MEMORY_MANAGER_SYSTEM_PROMPT, +// task_verifier_prompt: TASK_VERIFIER_SYSTEM_PROMPT, +// }, +// graph: { +// ...agent_config.graph, +// model: modelInstance, +// }, +// }; +// const snakAgent: SnakAgent = new SnakAgent(starknetConfig, agent, { +// database: process.env.POSTGRES_DB as string, +// host: process.env.POSTGRES_HOST as string, +// user: process.env.POSTGRES_USER as string, +// password: process.env.POSTGRES_PASSWORD as string, +// port: parseInt(process.env.POSTGRES_PORT as string), +// }); +// const autonomousAgent = new Graph(snakAgent); +// const app = await autonomousAgent.initialize(); +// return app; +// } -// Example usage with specific IDs (for backward compatibility) -const AUTONOMOUS_ID = '223d72b7-7b61-43af-bbf6-278e69994b3f'; +// // Example usage with specific IDs (for backward compatibility) +// const AUTONOMOUS_ID = '223d72b7-7b61-43af-bbf6-278e69994b3f'; -export const studio_graph_autonomous = () => - createAutonomousAgent(AUTONOMOUS_ID); +// export const studio_graph_autonomous = () => +// createAutonomousAgent(AUTONOMOUS_ID); diff --git a/packages/core/src/common/agent/interfaces/agent.interface.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts index de64f1002..ebce0adbe 100644 --- a/packages/core/src/common/agent/interfaces/agent.interface.ts +++ b/packages/core/src/common/agent/interfaces/agent.interface.ts @@ -194,12 +194,6 @@ export namespace AgentConfig { : Input; } -export interface StarknetConfig { - provider: RpcProvider; - accountPublicKey: string; - accountPrivateKey: string; -} - export interface DatabaseCredentials { host: string; port: number; diff --git a/packages/core/src/config/guards/guardsSchema.ts b/packages/core/src/config/guards/guardsSchema.ts index 2bbd80761..75b9a5a53 100644 --- a/packages/core/src/config/guards/guardsSchema.ts +++ b/packages/core/src/config/guards/guardsSchema.ts @@ -127,10 +127,8 @@ const McpServersConfigSchema = z.object({ // Agent graph model configuration schema const AgentGraphModelConfigSchema = z.object({ - provider_max_length: positiveInteger, - provider_min_length: positiveInteger, - model_name_max_length: positiveInteger, - model_name_min_length: positiveInteger, + allowed_provider: z.string(), + allowed_models: z.array(z.string()), max_temperature: z.number().min(0).max(1), max_tokens: positiveInteger, }); diff --git a/packages/core/src/services/agent-validation.service.ts b/packages/core/src/services/agent-validation.service.ts index 553f40e4b..003c890a7 100644 --- a/packages/core/src/services/agent-validation.service.ts +++ b/packages/core/src/services/agent-validation.service.ts @@ -267,45 +267,25 @@ export class AgentValidationService { */ private validateModelConfig(model: any): void { // Load guard values once for performance - const providerMaxLength = getGuardValue( - 'agents.graph.model.provider_max_length' - ); - const providerMinLength = getGuardValue( - 'agents.graph.model.provider_min_length' - ); - const modelNameMaxLength = getGuardValue( - 'agents.graph.model.model_name_max_length' - ); - const modelNameMinLength = getGuardValue( - 'agents.graph.model.model_name_min_length' - ); + const allowedProvider = getGuardValue('agents.graph.model.allowed_provider'); + const allowedModels = getGuardValue('agents.graph.model.allowed_models'); const maxTemperature = getGuardValue('agents.graph.model.max_temperature'); const maxTokens = getGuardValue('agents.graph.model.max_tokens'); // Validate provider if (model.provider) { - if (model.provider.length > providerMaxLength) { - throw new Error( - `Model provider too long. Maximum length: ${providerMaxLength}` - ); - } - if (model.provider.length < providerMinLength) { + if (model.provider.toLowerCase() !== allowedProvider.toLowerCase()) { throw new Error( - `Model provider too short. Minimum length: ${providerMinLength}` + `Invalid model provider. Only '${allowedProvider}' is supported.` ); } } // Validate model_name if (model.model_name) { - if (model.model_name.length > modelNameMaxLength) { - throw new Error( - `Model name too long. Maximum length: ${modelNameMaxLength}` - ); - } - if (model.model_name.length < modelNameMinLength) { + if (!allowedModels.includes(model.model_name)) { throw new Error( - `Model name too short. Minimum length: ${modelNameMinLength}` + `Invalid model name. Supported models: ${allowedModels.join(', ')}` ); } } diff --git a/packages/server/config/configuration.ts b/packages/server/config/configuration.ts index 7fe1fce28..8cd8b2238 100644 --- a/packages/server/config/configuration.ts +++ b/packages/server/config/configuration.ts @@ -1,6 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { RpcProvider } from 'starknet'; import { envSchema, type EnvConfig } from './env.validation.js'; import { RagConfigSize } from '@snakagent/core'; // Assuming core exports these types @@ -16,18 +15,14 @@ export class ConfigurationService { NODE_ENV: this.configService.get('NODE_ENV'), SERVER_PORT: this.configService.get('SERVER_PORT'), SERVER_API_KEY: this.configService.get('SERVER_API_KEY'), - STARKNET_PRIVATE_KEY: this.configService.get( - 'STARKNET_PRIVATE_KEY' - ), - STARKNET_PUBLIC_ADDRESS: this.configService.get( - 'STARKNET_PUBLIC_ADDRESS' - ), - STARKNET_RPC_URL: this.configService.get('STARKNET_RPC_URL'), AI_MODEL_LEVEL: this.configService.get('AI_MODEL_LEVEL'), AI_MODELS_CONFIG_PATH: this.configService.get( 'AI_MODELS_CONFIG_PATH' ), GEMINI_API_KEY: this.configService.get('GEMINI_API_KEY'), + DEFAULT_MODEL_PROVIDER: this.configService.get('DEFAULT_MODEL_PROVIDER'), + DEFAULT_MODEL_NAME: this.configService.get('DEFAULT_MODEL_NAME'), + DEFAULT_TEMPERATURE: this.configService.get('DEFAULT_TEMPERATURE'), GUARDS_CONFIG_PATH: this.configService.get('GUARDS_CONFIG_PATH'), REDIS_HOST: this.configService.get('REDIS_HOST'), REDIS_PORT: this.configService.get('REDIS_PORT'), @@ -87,13 +82,6 @@ export class ConfigurationService { return this.config.SERVER_API_KEY; } - get starknet() { - return { - privateKey: this.config.STARKNET_PRIVATE_KEY, - publicKey: this.config.STARKNET_PUBLIC_ADDRESS, - provider: new RpcProvider({ nodeUrl: this.config.STARKNET_RPC_URL }), - }; - } get rag() { return this.ragConfig; } @@ -128,4 +116,12 @@ export class ConfigurationService { get geminiApiKey(): string { return this.config.GEMINI_API_KEY; } + + get defaultModel() { + return { + provider: this.config.DEFAULT_MODEL_PROVIDER, + name: this.config.DEFAULT_MODEL_NAME, + temperature: this.config.DEFAULT_TEMPERATURE, + }; + } } diff --git a/packages/server/config/env.validation.ts b/packages/server/config/env.validation.ts index 2b68e6f94..40bd7b67e 100644 --- a/packages/server/config/env.validation.ts +++ b/packages/server/config/env.validation.ts @@ -7,10 +7,6 @@ export const envSchema = z.object({ SERVER_PORT: z.coerce.number().default(3000), SERVER_API_KEY: z.string(), - STARKNET_PRIVATE_KEY: z.string(), - STARKNET_PUBLIC_ADDRESS: z.string(), - STARKNET_RPC_URL: z.string().url(), - AI_MODEL_LEVEL: z.string().optional().default('smart'), AI_MODELS_CONFIG_PATH: z .string() @@ -19,6 +15,12 @@ export const envSchema = z.object({ // Provider-specific API Keys GEMINI_API_KEY: z.string(), + + // Default model configuration + DEFAULT_MODEL_PROVIDER: z.string(), + DEFAULT_MODEL_NAME: z.string(), + DEFAULT_TEMPERATURE: z.coerce.number().min(0).max(1), + // Rag max size configuration RAG_CONFIG_PATH: z.string().optional().default('config/rag/default.rag.json'), diff --git a/packages/server/src/agents.storage.ts b/packages/server/src/agents.storage.ts index cafadc188..3f11dc55e 100644 --- a/packages/server/src/agents.storage.ts +++ b/packages/server/src/agents.storage.ts @@ -7,7 +7,6 @@ import { RedisClient } from '@snakagent/database/redis'; import { AgentConfig, ModelConfig, - StarknetConfig, AgentPromptsInitialized, DEFAULT_AGENT_MODEL, AgentValidationService, @@ -549,11 +548,6 @@ export class AgentStorage implements OnModuleInit { agentConfig: AgentConfig.OutputWithId ): Promise { try { - const starknetConfig: StarknetConfig = { - provider: this.config.starknet.provider, - accountPrivateKey: this.config.starknet.privateKey, - accountPublicKey: this.config.starknet.publicKey, - }; const AgentConfigRuntime = await this.createAgentConfigRuntimeFromOutputWithId(agentConfig); if (!AgentConfigRuntime) { @@ -566,7 +560,7 @@ export class AgentStorage implements OnModuleInit { await supervisorAgent.init(); return supervisorAgent; } - const snakAgent = new SnakAgent(starknetConfig, AgentConfigRuntime); + const snakAgent = new SnakAgent(AgentConfigRuntime); await snakAgent.init(); return snakAgent; diff --git a/packages/workers/package.json b/packages/workers/package.json index dbfca63b6..1026d79be 100644 --- a/packages/workers/package.json +++ b/packages/workers/package.json @@ -33,7 +33,6 @@ "ioredis": "^5.3.2", "mammoth": "^1.6.0", "pdfjs-dist": "^4.0.0", - "starknet": "^6.0.0", "zod": "^3.24.2" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94ff5a56d..5f98ea29b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -89,9 +89,6 @@ importers: socket.io: specifier: ^4.8.1 version: 4.8.1 - starknet: - specifier: ^6.24.1 - version: 6.24.1(encoding@0.1.13) util: specifier: ^0.12.5 version: 0.12.5 @@ -413,9 +410,6 @@ importers: pdfjs-dist: specifier: ^4.0.0 version: 4.10.38 - starknet: - specifier: ^6.0.0 - version: 6.24.1(encoding@0.1.13) zod: specifier: ^3.24.2 version: 3.25.76 @@ -2310,18 +2304,10 @@ packages: '@noble/curves@1.2.0': resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} - '@noble/curves@1.7.0': - resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} - engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.3.2': resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} engines: {node: '>= 16'} - '@noble/hashes@1.6.0': - resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==} - engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.8.0': resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} @@ -2686,12 +2672,6 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - '@scure/base@1.2.1': - resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==} - - '@scure/starknet@1.1.0': - resolution: {integrity: sha512-83g3M6Ix2qRsPN4wqLDqiRZ2GBNbjVWfboJE/9UjfG+MHr6oDSu/CWgy8hsBSJejr09DkkL+l0Ze4KVrlCIdtQ==} - '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -2740,9 +2720,6 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} - '@starknet-io/types-js@0.7.10': - resolution: {integrity: sha512-1VtCqX4AHWJlRRSYGSn+4X1mqolI1Tdq62IwzoU2vUuEE72S1OlEeGhpvd6XsdqXcfHmVzYfj8k1XtKBQqwo9w==} - '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} @@ -3076,10 +3053,6 @@ packages: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - abi-wan-kanabi@2.2.4: - resolution: {integrity: sha512-0aA81FScmJCPX+8UvkXLki3X1+yPQuWxEkqXBVKltgPAK79J+NB+Lp5DouMXa7L6f+zcRlIA/6XO7BN/q9fnvg==} - hasBin: true - abort-controller-x@0.4.3: resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} @@ -3199,9 +3172,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - ansicolors@0.3.2: - resolution: {integrity: sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg==} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -3462,10 +3432,6 @@ packages: caniuse-lite@1.0.30001749: resolution: {integrity: sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==} - cardinal@2.1.1: - resolution: {integrity: sha512-JSr5eOgoEymtYHBjNWyjrMqet9Am2miJhlfKNdqLp6zoeAh0KN5dRAcxlecj5mAJrmQomgiOBj35xHLrFjqBpw==} - hasBin: true - chalk@4.1.0: resolution: {integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==} engines: {node: '>=10'} @@ -4379,9 +4345,6 @@ packages: fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} - fetch-cookie@3.0.1: - resolution: {integrity: sha512-ZGXe8Y5Z/1FWqQ9q/CrJhkUD73DyBU9VF0hBQmEO/wPHe4A9PKTjplFDLeFX8aOsYypZUcX5Ji/eByn3VCVO3Q==} - fflate@0.8.2: resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} @@ -5042,9 +5005,6 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - isomorphic-fetch@3.0.0: - resolution: {integrity: sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==} - isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} @@ -5605,9 +5565,6 @@ packages: lop@0.4.2: resolution: {integrity: sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw==} - lossless-json@4.1.1: - resolution: {integrity: sha512-HusN80C0ohtT9kOHQH7EuUaqzRQsnekpa+2ot8OzvW0iC08dq/YtM/7uKwwajldQsCrHyC8q9fz3t3L+TmDltA==} - lowdb@1.0.0: resolution: {integrity: sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==} engines: {node: '>=4'} @@ -6237,9 +6194,6 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - pako@2.1.0: - resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6687,9 +6641,6 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - redeyed@2.1.1: - resolution: {integrity: sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ==} - redis-errors@1.2.0: resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} engines: {node: '>=4'} @@ -7113,9 +7064,6 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - starknet@6.24.1: - resolution: {integrity: sha512-g7tiCt73berhcNi41otlN3T3kxZnIvZhMi8WdC21Y6GC6zoQgbI2z1t7JAZF9c4xZiomlanwVnurcpyfEdyMpg==} - statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -9747,14 +9695,8 @@ snapshots: dependencies: '@noble/hashes': 1.3.2 - '@noble/curves@1.7.0': - dependencies: - '@noble/hashes': 1.6.0 - '@noble/hashes@1.3.2': {} - '@noble/hashes@1.6.0': {} - '@noble/hashes@1.8.0': {} '@nodelib/fs.scandir@2.1.5': @@ -10119,13 +10061,6 @@ snapshots: '@scarf/scarf@1.4.0': {} - '@scure/base@1.2.1': {} - - '@scure/starknet@1.1.0': - dependencies: - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.0 - '@sec-ant/readable-stream@0.4.1': {} '@sideway/address@4.1.5': @@ -10180,8 +10115,6 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@starknet-io/types-js@0.7.10': {} - '@tokenizer/inflate@0.2.7': dependencies: debug: 4.4.1 @@ -10614,13 +10547,6 @@ snapshots: abbrev@2.0.0: {} - abi-wan-kanabi@2.2.4: - dependencies: - ansicolors: 0.3.2 - cardinal: 2.1.1 - fs-extra: 10.1.0 - yargs: 17.7.2 - abort-controller-x@0.4.3: {} abort-controller@3.0.0: @@ -10731,8 +10657,6 @@ snapshots: ansi-styles@6.2.3: {} - ansicolors@0.3.2: {} - any-promise@1.3.0: {} anymatch@3.1.3: @@ -11078,11 +11002,6 @@ snapshots: caniuse-lite@1.0.30001749: {} - cardinal@2.1.1: - dependencies: - ansicolors: 0.3.2 - redeyed: 2.1.1 - chalk@4.1.0: dependencies: ansi-styles: 4.3.0 @@ -12116,11 +12035,6 @@ snapshots: fecha@4.2.3: {} - fetch-cookie@3.0.1: - dependencies: - set-cookie-parser: 2.7.1 - tough-cookie: 4.1.4 - fflate@0.8.2: {} figures@3.2.0: @@ -12868,13 +12782,6 @@ snapshots: isobject@3.0.1: {} - isomorphic-fetch@3.0.0(encoding@0.1.13): - dependencies: - node-fetch: 2.7.0(encoding@0.1.13) - whatwg-fetch: 3.6.20 - transitivePeerDependencies: - - encoding - isstream@0.1.2: {} istanbul-lib-coverage@3.2.2: {} @@ -13723,8 +13630,6 @@ snapshots: option: 0.2.4 underscore: 1.13.7 - lossless-json@4.1.1: {} - lowdb@1.0.0: dependencies: graceful-fs: 4.2.11 @@ -14443,8 +14348,6 @@ snapshots: pako@1.0.11: {} - pako@2.1.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -14908,10 +14811,6 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - redeyed@2.1.1: - dependencies: - esprima: 4.0.1 - redis-errors@1.2.0: {} redis-parser@3.0.0: @@ -15402,22 +15301,6 @@ snapshots: standard-as-callback@2.1.0: {} - starknet@6.24.1(encoding@0.1.13): - dependencies: - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.0 - '@scure/base': 1.2.1 - '@scure/starknet': 1.1.0 - abi-wan-kanabi: 2.2.4 - fetch-cookie: 3.0.1 - isomorphic-fetch: 3.0.0(encoding@0.1.13) - lossless-json: 4.1.1 - pako: 2.1.0 - starknet-types-07: '@starknet-io/types-js@0.7.10' - ts-mixer: 6.0.4 - transitivePeerDependencies: - - encoding - statuses@2.0.1: {} statuses@2.0.2: {} diff --git a/tests/src/file-test/file-upload.ts b/tests/src/file-test/file-upload.ts index 7baf42ca4..e137bac71 100644 --- a/tests/src/file-test/file-upload.ts +++ b/tests/src/file-test/file-upload.ts @@ -23,7 +23,7 @@ async function testFiles() { await testRunner.runTest('Health Check', () => testRunner.client.healthCheck()); const createResult = await testRunner.runTest('Create Agent for File Testing', () => - testRunner.client.createAgent({ + testRunner.client.createAgent({~ agent: { name: 'File Test Agent', group: 'test', From 9556501ebe129fc39431642da87d6c2aa9508d63 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 13:46:36 +0000 Subject: [PATCH 06/18] Adding mcps and remove starknet --- packages/agent/src/agents/core/snakAgent.ts | 3 +- .../supervisor/tools/schemas/index.ts | 1 + .../tools/schemas/searchMcpServer.schema.ts | 26 ++ .../supervisor/tools/searchMcpServerTools.ts | 233 ++++++++++++++++++ .../agent/interfaces/agent.interface.ts | 25 +- packages/server/common/errors/error.type.ts | 4 - packages/server/common/errors/index.ts | 1 - .../server/common/errors/starknet.errors.ts | 14 -- packages/server/src/services/agent.service.ts | 16 -- packages/server/src/utils/agents.utils.ts | 8 +- 10 files changed, 266 insertions(+), 65 deletions(-) create mode 100644 packages/agent/src/agents/operators/supervisor/tools/schemas/searchMcpServer.schema.ts create mode 100644 packages/agent/src/agents/operators/supervisor/tools/searchMcpServerTools.ts delete mode 100644 packages/server/common/errors/starknet.errors.ts diff --git a/packages/agent/src/agents/core/snakAgent.ts b/packages/agent/src/agents/core/snakAgent.ts index 6633582d6..c449da04e 100644 --- a/packages/agent/src/agents/core/snakAgent.ts +++ b/packages/agent/src/agents/core/snakAgent.ts @@ -1,5 +1,4 @@ import { BaseAgent } from './baseAgent.js'; -import { RpcProvider } from 'starknet'; import { logger, AgentConfig } from '@snakagent/core'; import { BaseMessage, HumanMessage } from '@langchain/core/messages'; import { AgentType } from '../../shared/enums/agent.enum.js'; @@ -116,7 +115,7 @@ export class SnakAgent extends BaseAgent { } return this.ragAgent; } - + public async dispose(): Promise { this.stop(); if (this.pgCheckpointer) { diff --git a/packages/agent/src/agents/operators/supervisor/tools/schemas/index.ts b/packages/agent/src/agents/operators/supervisor/tools/schemas/index.ts index a97468ed9..bf4558a67 100644 --- a/packages/agent/src/agents/operators/supervisor/tools/schemas/index.ts +++ b/packages/agent/src/agents/operators/supervisor/tools/schemas/index.ts @@ -5,3 +5,4 @@ export * from './common.schemas.js'; export * from './createAgent.schema.js'; export * from './updateAgent.schema.js'; export * from './message_ask_user.schema.js'; +export * from './searchMcpServer.schema.js'; diff --git a/packages/agent/src/agents/operators/supervisor/tools/schemas/searchMcpServer.schema.ts b/packages/agent/src/agents/operators/supervisor/tools/schemas/searchMcpServer.schema.ts new file mode 100644 index 000000000..c2fb862a7 --- /dev/null +++ b/packages/agent/src/agents/operators/supervisor/tools/schemas/searchMcpServer.schema.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; +import { getGuardValue } from '@snakagent/core'; + +export const SearchMcpServerSchema = z.object({ + query: z + .string() + .max(getGuardValue('mcp.max_query_length')) + .describe( + 'Human readable search query for MCP servers (e.g., "web search", "file management", "memory")' + ), + limit: z + .number() + .max(getGuardValue('mcp.max_limit_tools')) + .optional() + .describe('Maximum number of results to return (default: 10)'), + deployedOnly: z + .boolean() + .optional() + .describe('Only return deployed servers (default: false)'), + verifiedOnly: z + .boolean() + .optional() + .describe('Only return verified servers (default: false)'), +}); + +export type SearchMcpServerInput = z.infer; diff --git a/packages/agent/src/agents/operators/supervisor/tools/searchMcpServerTools.ts b/packages/agent/src/agents/operators/supervisor/tools/searchMcpServerTools.ts new file mode 100644 index 000000000..2e9548dc1 --- /dev/null +++ b/packages/agent/src/agents/operators/supervisor/tools/searchMcpServerTools.ts @@ -0,0 +1,233 @@ +import { DynamicStructuredTool } from '@langchain/core/tools'; +import { logger } from '@snakagent/core'; +import { SearchMcpServerSchema } from './schemas/index.js'; + +interface SmitheryServerResponse { + qualifiedName: string; + displayName: string; + description: string; + homepage: string; + useCount: string; + isDeployed: boolean; + createdAt: string; +} + +interface SmitheryListResponse { + servers: SmitheryServerResponse[]; + pagination: { + currentPage: number; + pageSize: number; + totalPages: number; + totalCount: number; + }; +} + +interface SmitheryServerDetail { + qualifiedName: string; + displayName: string; + iconUrl: string | null; + deploymentUrl: string; + connections: Array<{ + type: string; + url?: string; + configSchema: any; + }>; + security: { + scanPassed: boolean; + } | null; + tools: Array<{ + name: string; + description: string | null; + inputSchema: { + type: 'object'; + properties?: object; + }; + }> | null; +} + +export function searchMcpServerTool(): DynamicStructuredTool { + return new DynamicStructuredTool({ + name: 'search_mcp_server', + description: + 'Search for MCP servers on Smithery using a human readable search request', + schema: SearchMcpServerSchema, + func: async ({ + query, + limit = 10, + deployedOnly = false, + verifiedOnly = false, + }) => { + try { + const apiKey = process.env.SMITHERY_API_KEY; + if (!apiKey) { + throw new Error('SMITHERY_API_KEY environment variable is required'); + } + + let searchQuery = query; + if (deployedOnly) searchQuery += ' is:deployed'; + if (verifiedOnly) searchQuery += ' is:verified'; + + const searchParams = new URLSearchParams({ + q: searchQuery, + page: '1', + pageSize: limit.toString(), + }); + + const response = await fetch( + `https://registry.smithery.ai/servers?${searchParams.toString()}`, + { + headers: { + Authorization: `Bearer ${apiKey}`, + Accept: 'application/json', + }, + } + ); + + if (!response.ok) { + if (response.status === 401) { + throw new Error( + 'Invalid Smithery API key. Please check your SMITHERY_API_KEY environment variable.' + ); + } + throw new Error( + `Smithery API request failed: ${response.status} ${response.statusText}` + ); + } + + if (response.bodyUsed) { + throw new Error( + 'Response body already consumed in main search request' + ); + } + const searchResult: SmitheryListResponse = await response.json(); + + if (!searchResult.servers || searchResult.servers.length === 0) { + return JSON.stringify( + { + success: true, + message: 'No MCP servers found matching your query', + query: query, + servers: [], + totalCount: 0, + }, + null, + 2 + ); + } + + const serverDetails = await Promise.all( + searchResult.servers.map(async (server) => { + try { + const detailResponse = await fetch( + `https://registry.smithery.ai/servers/${encodeURIComponent(server.qualifiedName)}`, + { + headers: { + Authorization: `Bearer ${apiKey}`, + Accept: 'application/json', + }, + } + ); + + if (!detailResponse.ok) { + logger.warn( + `Failed to get details for server ${server.qualifiedName}: ${detailResponse.status}` + ); + return { + ...server, + connections: [], + tools: [], + configSchema: null, + }; + } + + if (detailResponse.bodyUsed) { + throw new Error( + `Response body already consumed for server ${server.qualifiedName}` + ); + } + const detail: SmitheryServerDetail = await detailResponse.json(); + + const httpConnection = detail.connections.find( + (conn) => conn.type === 'http' + ); + const stdioConnection = detail.connections.find( + (conn) => conn.type === 'stdio' + ); + + return { + qualifiedName: server.qualifiedName, + displayName: server.displayName, + description: server.description, + homepage: server.homepage, + useCount: server.useCount, + isDeployed: server.isDeployed, + isVerified: detail.security?.scanPassed || false, + tools: detail.tools || [], + toolCount: detail.tools?.length || 0, + connections: detail.connections.map((conn) => ({ + type: conn.type, + url: conn.url, + hasConfig: !!( + conn.configSchema?.properties && + Object.keys(conn.configSchema.properties).length > 0 + ), + requiredFields: conn.configSchema?.required || [], + configFields: conn.configSchema?.properties + ? Object.keys(conn.configSchema.properties) + : [], + })), + installation: { + isRemote: server.isDeployed && httpConnection, + requiresApiKey: server.isDeployed, + hasLocalOption: !!stdioConnection, + configurationRequired: !!( + httpConnection?.configSchema?.required?.length || + stdioConnection?.configSchema?.required?.length + ), + }, + }; + } catch (error) { + logger.error( + `Error getting details for server ${server.qualifiedName}: ${error}` + ); + return { + ...server, + connections: [], + tools: [], + installation: { + isRemote: false, + requiresApiKey: false, + hasLocalOption: false, + configurationRequired: false, + }, + }; + } + }) + ); + + return JSON.stringify( + { + success: true, + message: `Found ${searchResult.servers.length} matching MCP servers`, + query: query, + totalCount: searchResult.pagination.totalCount, + currentPage: searchResult.pagination.currentPage, + totalPages: searchResult.pagination.totalPages, + servers: serverDetails, + usage: { + tip: "Use 'install_mcp_server' to install any of these servers for an agent", + note: 'Remote servers require a Smithery API key, local servers can run without one', + configHelp: + "Check 'installation.configurationRequired' to see if additional configuration is needed", + }, + }, + null, + 2 + ); + } catch (error) { + logger.error(`Error searching MCP servers: ${error}`); + throw new Error(`Failed to search MCP servers: ${error}`); + } + }, + }); +} diff --git a/packages/core/src/common/agent/interfaces/agent.interface.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts index ebce0adbe..a151fb9ed 100644 --- a/packages/core/src/common/agent/interfaces/agent.interface.ts +++ b/packages/core/src/common/agent/interfaces/agent.interface.ts @@ -1,4 +1,3 @@ -import { RpcProvider } from 'starknet'; import { BaseChatModel } from '@langchain/core/language_models/chat_models'; export type Modify = Omit & R; @@ -200,26 +199,4 @@ export interface DatabaseCredentials { user: string; password: string; database: string; -} - -// TODO REMOVE WHEN REMOVED PLUGINS -/** - * @interface SnakAgentInterface - * @description Interface for the Starknet agent - * @property {() => { accountPublicKey: string; accountPrivateKey: string; }} getAccountCredentials - Function to get the account credentials - * @property {() => DatabaseCredentials} getDatabaseCredentials - Function to get the database credentials - * @property {() => RpcProvider} getProvider - Function to get the provider - * @property {() => AgentConfigInput} getAgentConfig - Function to get the agent configuration - * @property {(database_name: string) => Promise} connectDatabase - Function to connect to a database - * @property {(database_name: string) => Promise} createDatabase - Function to create a database - * @property {(name: string) => PostgresAdaptater | undefined} getDatabaseByName - Function to get a database by name - */ -export interface SnakAgentInterface { - getAccountCredentials: () => { - accountPublicKey: string; - accountPrivateKey: string; - }; - getDatabaseCredentials: () => DatabaseCredentials; - getProvider: () => RpcProvider; - getAgentConfig: () => AgentConfig.Input; -} +} \ No newline at end of file diff --git a/packages/server/common/errors/error.type.ts b/packages/server/common/errors/error.type.ts index fb3181a07..9d171d2a6 100644 --- a/packages/server/common/errors/error.type.ts +++ b/packages/server/common/errors/error.type.ts @@ -5,10 +5,6 @@ export enum ErrorType { UNAUTHORIZED = 'UNAUTHORIZED', FORBIDDEN = 'FORBIDDEN', - // Starknet Errors - STARKNET_TRANSACTION_ERROR = 'STARKNET_TRANSACTION_ERROR', - STARKNET_RPC_ERROR = 'STARKNET_RPC_ERROR', - // Agent Errors AGENT_EXECUTION_ERROR = 'AGENT_EXECUTION_ERROR', AGENT_INITIALIZATION_ERROR = 'AGENT_INITIALIZATION_ERROR', diff --git a/packages/server/common/errors/index.ts b/packages/server/common/errors/index.ts index 6d39a7ced..a9ec0a9a0 100644 --- a/packages/server/common/errors/index.ts +++ b/packages/server/common/errors/index.ts @@ -1,6 +1,5 @@ export * from './error.type.js'; export * from './base.error.js'; export * from './application.errors.js'; -export * from './starknet.errors.js'; export * from './agent.errors.js'; export * from './job.errors.js'; diff --git a/packages/server/common/errors/starknet.errors.ts b/packages/server/common/errors/starknet.errors.ts deleted file mode 100644 index ce5b49605..000000000 --- a/packages/server/common/errors/starknet.errors.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BaseError } from './base.error.js'; -import { ErrorType, ErrorMetadata } from './error.type.js'; - -export class StarknetTransactionError extends BaseError { - constructor(message: string, metadata?: ErrorMetadata) { - super(ErrorType.STARKNET_TRANSACTION_ERROR, message, metadata); - } -} - -export class StarknetRpcError extends BaseError { - constructor(message: string, metadata?: ErrorMetadata) { - super(ErrorType.STARKNET_RPC_ERROR, message, metadata); - } -} diff --git a/packages/server/src/services/agent.service.ts b/packages/server/src/services/agent.service.ts index a0388eeec..51b9f3cac 100644 --- a/packages/server/src/services/agent.service.ts +++ b/packages/server/src/services/agent.service.ts @@ -12,7 +12,6 @@ import { AgentExecutionError, } from '../../common/errors/agent.errors.js'; import { ConfigurationService } from '../../config/configuration.js'; -import { StarknetTransactionError } from '../../common/errors/starknet.errors.js'; import { BaseAgent, ChunkOutput, @@ -132,14 +131,6 @@ export class AgentService { if (error instanceof AgentValidationError) { throw error; } - - if (error.message?.includes('transaction')) { - throw new StarknetTransactionError('Failed to execute transaction', { - originalError: error.message, - cause: error, - }); - } - throw new AgentExecutionError('Failed to process agent request', { originalError: error.message, cause: error, @@ -191,13 +182,6 @@ export class AgentService { throw error; } - if (error.message?.includes('transaction')) { - throw new StarknetTransactionError('Failed to execute transaction', { - originalError: error.message, - cause: error, - }); - } - throw new AgentExecutionError('Failed to process agent request', { originalError: error.message, cause: error, diff --git a/packages/server/src/utils/agents.utils.ts b/packages/server/src/utils/agents.utils.ts index c14ec1696..93da8d344 100644 --- a/packages/server/src/utils/agents.utils.ts +++ b/packages/server/src/utils/agents.utils.ts @@ -1,8 +1,6 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; -import { ModelConfig } from '@snakagent/core'; -import { logger } from 'starknet'; - +import { ModelConfig, logger } from '@snakagent/core'; const SUPPORTED_GEMINI_MODELS = ['gemini-2.5-flash', 'gemini-2.5-pro']; /** @@ -20,7 +18,9 @@ export function initializeModels(model: ModelConfig): BaseChatModel | null { // Only support Gemini provider if (model.provider.toLowerCase() !== 'gemini') { - throw new Error(`Unsupported provider: ${model.provider}. Only 'gemini' is supported.`); + throw new Error( + `Unsupported provider: ${model.provider}. Only 'gemini' is supported.` + ); } // Validate model name From 809e8cd3903fbb1f207881e1da499da933b1ed56 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 13:49:33 +0000 Subject: [PATCH 07/18] Adding the smithery apik ey mandatory --- .env.example | 1 + .../agent/src/agents/operators/supervisor/supervisorTools.ts | 2 ++ packages/server/config/env.validation.ts | 1 + 3 files changed, 4 insertions(+) diff --git a/.env.example b/.env.example index 64d35a1e7..af99db28d 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,7 @@ LANGSMITH_PROJECT="Snak" # --- AI Model Configuration (mandatory) --- GEMINI_API_KEY="YOUR_GEMINI_API_KEY" +SMITHERY_API_KEY="YOUR_SMITHERY_API_KEY" # --- Redis Configuration (mandatory) --- REDIS_HOST=localhost diff --git a/packages/agent/src/agents/operators/supervisor/supervisorTools.ts b/packages/agent/src/agents/operators/supervisor/supervisorTools.ts index 20cdf197b..05f55f1fe 100644 --- a/packages/agent/src/agents/operators/supervisor/supervisorTools.ts +++ b/packages/agent/src/agents/operators/supervisor/supervisorTools.ts @@ -11,6 +11,7 @@ import { updateMcpServerTool, messageAskUserTool, } from './tools/index.js'; +import { searchMcpServerTool } from './tools/searchMcpServerTools.js'; /** * Shared configuration tools reserved for supervisor agents. @@ -50,6 +51,7 @@ export function getMcpServerHelperTools(agentConfig: AgentConfig.Runtime) { addMcpServerTool(agentConfig), removeMcpServerTool(agentConfig), updateMcpServerTool(agentConfig), + searchMcpServerTool(), ]; } diff --git a/packages/server/config/env.validation.ts b/packages/server/config/env.validation.ts index 40bd7b67e..2471b03b3 100644 --- a/packages/server/config/env.validation.ts +++ b/packages/server/config/env.validation.ts @@ -15,6 +15,7 @@ export const envSchema = z.object({ // Provider-specific API Keys GEMINI_API_KEY: z.string(), + SMITHERY_API_KEY: z.string(), // Default model configuration DEFAULT_MODEL_PROVIDER: z.string(), From f3bc3abf43062d72b4df7aa878f4bc717e0092bb Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 18:29:09 +0000 Subject: [PATCH 08/18] prettier all --- .../tools/schemas/common.schemas.ts | 10 +++++++--- .../agent/interfaces/agent.interface.ts | 2 +- .../src/services/agent-validation.service.ts | 4 +++- packages/server/config/configuration.ts | 19 +++++++++++++------ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts index 155680c30..feb0a8fe4 100644 --- a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts +++ b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts @@ -38,9 +38,13 @@ const modelGuardsValues: GuardsConfig['agents']['graph']['model'] = export const ModelConfigSchema = z.object({ provider: z .string() - .refine((val) => val.toLowerCase() === modelGuardsValues.allowed_provider.toLowerCase(), { - message: `Provider must be '${modelGuardsValues.allowed_provider}'`, - }) + .refine( + (val) => + val.toLowerCase() === modelGuardsValues.allowed_provider.toLowerCase(), + { + message: `Provider must be '${modelGuardsValues.allowed_provider}'`, + } + ) .optional() .describe('Model provider'), model_name: z diff --git a/packages/core/src/common/agent/interfaces/agent.interface.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts index a151fb9ed..47f5b9ada 100644 --- a/packages/core/src/common/agent/interfaces/agent.interface.ts +++ b/packages/core/src/common/agent/interfaces/agent.interface.ts @@ -199,4 +199,4 @@ export interface DatabaseCredentials { user: string; password: string; database: string; -} \ No newline at end of file +} diff --git a/packages/core/src/services/agent-validation.service.ts b/packages/core/src/services/agent-validation.service.ts index 003c890a7..83afd432d 100644 --- a/packages/core/src/services/agent-validation.service.ts +++ b/packages/core/src/services/agent-validation.service.ts @@ -267,7 +267,9 @@ export class AgentValidationService { */ private validateModelConfig(model: any): void { // Load guard values once for performance - const allowedProvider = getGuardValue('agents.graph.model.allowed_provider'); + const allowedProvider = getGuardValue( + 'agents.graph.model.allowed_provider' + ); const allowedModels = getGuardValue('agents.graph.model.allowed_models'); const maxTemperature = getGuardValue('agents.graph.model.max_temperature'); const maxTokens = getGuardValue('agents.graph.model.max_tokens'); diff --git a/packages/server/config/configuration.ts b/packages/server/config/configuration.ts index 8cd8b2238..a3fc643df 100644 --- a/packages/server/config/configuration.ts +++ b/packages/server/config/configuration.ts @@ -20,9 +20,13 @@ export class ConfigurationService { 'AI_MODELS_CONFIG_PATH' ), GEMINI_API_KEY: this.configService.get('GEMINI_API_KEY'), - DEFAULT_MODEL_PROVIDER: this.configService.get('DEFAULT_MODEL_PROVIDER'), + DEFAULT_MODEL_PROVIDER: this.configService.get( + 'DEFAULT_MODEL_PROVIDER' + ), DEFAULT_MODEL_NAME: this.configService.get('DEFAULT_MODEL_NAME'), - DEFAULT_TEMPERATURE: this.configService.get('DEFAULT_TEMPERATURE'), + DEFAULT_TEMPERATURE: this.configService.get( + 'DEFAULT_TEMPERATURE' + ), GUARDS_CONFIG_PATH: this.configService.get('GUARDS_CONFIG_PATH'), REDIS_HOST: this.configService.get('REDIS_HOST'), REDIS_PORT: this.configService.get('REDIS_PORT'), @@ -45,12 +49,15 @@ export class ConfigurationService { } }); - const formattedError = errorMessages.length > 0 - ? `\n\nMissing or invalid environment variables:\n${errorMessages.join('\n')}\n\nPlease check your .env file and ensure all required variables are set.\n` - : JSON.stringify(errors, null, 2); + const formattedError = + errorMessages.length > 0 + ? `\n\nMissing or invalid environment variables:\n${errorMessages.join('\n')}\n\nPlease check your .env file and ensure all required variables are set.\n` + : JSON.stringify(errors, null, 2); this.logger.error(formattedError); - throw new Error('Invalid environment variables. Check logs above for details.'); + throw new Error( + 'Invalid environment variables. Check logs above for details.' + ); } this.config = result.data; From 1abdc9996d4fd38ad8fbae9404313a40a61998fd Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Mon, 27 Oct 2025 21:23:03 +0000 Subject: [PATCH 09/18] version with the model_provider in the guard with verification --- .../src/services/agent-validation.service.ts | 30 ++++++++++--------- packages/server/config/configuration.ts | 5 ++++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/packages/core/src/services/agent-validation.service.ts b/packages/core/src/services/agent-validation.service.ts index 83afd432d..0e02eea86 100644 --- a/packages/core/src/services/agent-validation.service.ts +++ b/packages/core/src/services/agent-validation.service.ts @@ -274,22 +274,24 @@ export class AgentValidationService { const maxTemperature = getGuardValue('agents.graph.model.max_temperature'); const maxTokens = getGuardValue('agents.graph.model.max_tokens'); - // Validate provider - if (model.provider) { - if (model.provider.toLowerCase() !== allowedProvider.toLowerCase()) { - throw new Error( - `Invalid model provider. Only '${allowedProvider}' is supported.` - ); - } + // Validate provider - REQUIRED + if (!model.model_provider) { + throw new Error('Model provider is required'); + } + if (model.model_provider.toLowerCase() !== allowedProvider.toLowerCase()) { + throw new Error( + `Invalid model provider. Only '${allowedProvider}' is supported.` + ); } - // Validate model_name - if (model.model_name) { - if (!allowedModels.includes(model.model_name)) { - throw new Error( - `Invalid model name. Supported models: ${allowedModels.join(', ')}` - ); - } + // Validate model_name - REQUIRED + if (!model.model_name) { + throw new Error('Model name is required'); + } + if (!allowedModels.includes(model.model_name)) { + throw new Error( + `Invalid model name. Supported models: ${allowedModels.join(', ')}` + ); } // Validate temperature diff --git a/packages/server/config/configuration.ts b/packages/server/config/configuration.ts index a3fc643df..7bb6f1e01 100644 --- a/packages/server/config/configuration.ts +++ b/packages/server/config/configuration.ts @@ -20,6 +20,7 @@ export class ConfigurationService { 'AI_MODELS_CONFIG_PATH' ), GEMINI_API_KEY: this.configService.get('GEMINI_API_KEY'), + SMITHERY_API_KEY: this.configService.get('SMITHERY_API_KEY'), DEFAULT_MODEL_PROVIDER: this.configService.get( 'DEFAULT_MODEL_PROVIDER' ), @@ -124,6 +125,10 @@ export class ConfigurationService { return this.config.GEMINI_API_KEY; } + get smitheryApiKey(): string { + return this.config.SMITHERY_API_KEY; + } + get defaultModel() { return { provider: this.config.DEFAULT_MODEL_PROVIDER, From baa3aec2a5b724bb1c7f76a0892a965791d6aaa6 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 11:48:55 +0000 Subject: [PATCH 10/18] version with code rabbit usefull change requested --- .../src/agents/graphs/utils/graph.utils.ts | 2 +- .../tools/schemas/common.schemas.ts | 1 - ...nt.prompt.ts => hitl-constraint.prompt.ts} | 0 .../shared/prompts/agents/snak/hitl/index.ts | 2 +- .../common/server/dto/agent/message.dto.ts | 22 ++++--------------- packages/server/config/configuration.ts | 2 +- .../src/controllers/agents.controller.ts | 7 ------ packages/server/src/utils/agents.utils.ts | 1 + 8 files changed, 8 insertions(+), 29 deletions(-) rename packages/agent/src/shared/prompts/agents/snak/hitl/{hitl-contraint.prompt.ts => hitl-constraint.prompt.ts} (100%) diff --git a/packages/agent/src/agents/graphs/utils/graph.utils.ts b/packages/agent/src/agents/graphs/utils/graph.utils.ts index dd40fb37d..4b424d947 100644 --- a/packages/agent/src/agents/graphs/utils/graph.utils.ts +++ b/packages/agent/src/agents/graphs/utils/graph.utils.ts @@ -31,7 +31,7 @@ import { HITL_CONSTRAINT_LEVEL_2, HITL_CONSTRAINT_LEVEL_3, HITL_CONSTRAINT_LEVEL_4, -} from '@prompts/agents/snak/hitl/hitl-contraint.prompt.js'; +} from '@prompts/agents/snak/hitl/hitl-constraint.prompt.js'; // --- Response Generators --- export function createMaxIterationsResponse( graph_step: number, diff --git a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts index feb0a8fe4..81b020d7a 100644 --- a/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts +++ b/packages/agent/src/agents/operators/supervisor/tools/schemas/common.schemas.ts @@ -52,7 +52,6 @@ export const ModelConfigSchema = z.object({ .refine((val) => modelGuardsValues.allowed_models.includes(val), { message: `Model must be one of: ${modelGuardsValues.allowed_models.join(', ')}`, }) - .optional() .describe('Model name'), temperature: z .number() diff --git a/packages/agent/src/shared/prompts/agents/snak/hitl/hitl-contraint.prompt.ts b/packages/agent/src/shared/prompts/agents/snak/hitl/hitl-constraint.prompt.ts similarity index 100% rename from packages/agent/src/shared/prompts/agents/snak/hitl/hitl-contraint.prompt.ts rename to packages/agent/src/shared/prompts/agents/snak/hitl/hitl-constraint.prompt.ts diff --git a/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts b/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts index d5284fbec..8c30f01a6 100644 --- a/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts +++ b/packages/agent/src/shared/prompts/agents/snak/hitl/index.ts @@ -2,4 +2,4 @@ * Re-export all HITL prompts */ -export * from './hitl-contraint.prompt.js'; +export * from './hitl-constraint.prompt.js'; diff --git a/packages/core/src/common/server/dto/agent/message.dto.ts b/packages/core/src/common/server/dto/agent/message.dto.ts index 0cfec17a2..8f004ef6d 100644 --- a/packages/core/src/common/server/dto/agent/message.dto.ts +++ b/packages/core/src/common/server/dto/agent/message.dto.ts @@ -7,7 +7,9 @@ import { Min, Max, IsInt, + ValidateNested, } from 'class-validator'; +import { Type } from 'class-transformer'; /** * DTO for retrieving messages from a specific agent @@ -59,29 +61,13 @@ export class Message { @Length(1, 10000) content: string; } - export class AgentRequestDTO { @IsNotEmpty() + @ValidateNested() + @Type(() => Message) request: Message; } -export class SupervisorRequest { - @IsNotEmpty() - @IsString() - @Length(1, 10000) - content: string; - - @IsOptional() - @IsString() - @IsUUID() - agentId?: string; // Optional: specify which agent to use -} - -export class SupervisorRequestDTO { - @IsNotEmpty() - request: SupervisorRequest; -} - export class getMessagesFromAgentsDTO { @IsNotEmpty() @IsString() diff --git a/packages/server/config/configuration.ts b/packages/server/config/configuration.ts index 7bb6f1e01..13adaf14f 100644 --- a/packages/server/config/configuration.ts +++ b/packages/server/config/configuration.ts @@ -25,7 +25,7 @@ export class ConfigurationService { 'DEFAULT_MODEL_PROVIDER' ), DEFAULT_MODEL_NAME: this.configService.get('DEFAULT_MODEL_NAME'), - DEFAULT_TEMPERATURE: this.configService.get( + DEFAULT_TEMPERATURE: this.configService.get( 'DEFAULT_TEMPERATURE' ), GUARDS_CONFIG_PATH: this.configService.get('GUARDS_CONFIG_PATH'), diff --git a/packages/server/src/controllers/agents.controller.ts b/packages/server/src/controllers/agents.controller.ts index fdbac62a5..8409db3db 100644 --- a/packages/server/src/controllers/agents.controller.ts +++ b/packages/server/src/controllers/agents.controller.ts @@ -46,13 +46,6 @@ import { import { supervisorAgentConfig } from '@snakagent/core'; -export interface SupervisorRequestDTO { - request: { - content: string; - agent_id?: string; - }; -} - export interface AgentAvatarResponseDTO { id: string; avatar_mime_type: string; diff --git a/packages/server/src/utils/agents.utils.ts b/packages/server/src/utils/agents.utils.ts index 93da8d344..7b37e9f5f 100644 --- a/packages/server/src/utils/agents.utils.ts +++ b/packages/server/src/utils/agents.utils.ts @@ -1,6 +1,7 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; import { ModelConfig, logger } from '@snakagent/core'; + const SUPPORTED_GEMINI_MODELS = ['gemini-2.5-flash', 'gemini-2.5-pro']; /** From 8cb85ba664f13cc4e7b41a95b1ffa37dcc8ea6c7 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 14:17:50 +0000 Subject: [PATCH 11/18] all config put with gemini --- config/agents/cairo.agent.json | 4 ++-- config/agents/default.agent.json | 4 ++-- config/agents/ethereum-rpc.agent.json | 4 ++-- config/agents/example.agent.json | 4 ++-- config/agents/memory.agent.json | 4 ++-- config/agents/starknet-rpc.agent.json | 4 ++-- config/agents/supervisor.agent.json | 4 ++-- config/models/default.models.json | 12 ++++++------ 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/config/agents/cairo.agent.json b/config/agents/cairo.agent.json index e95f57a09..b4ecb7183 100644 --- a/config/agents/cairo.agent.json +++ b/config/agents/cairo.agent.json @@ -32,8 +32,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/default.agent.json b/config/agents/default.agent.json index d1dbc99d6..624116bdf 100644 --- a/config/agents/default.agent.json +++ b/config/agents/default.agent.json @@ -40,8 +40,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/ethereum-rpc.agent.json b/config/agents/ethereum-rpc.agent.json index 1feeb5f77..05f2757ac 100644 --- a/config/agents/ethereum-rpc.agent.json +++ b/config/agents/ethereum-rpc.agent.json @@ -45,8 +45,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/example.agent.json b/config/agents/example.agent.json index ff6b01790..6e1b22699 100644 --- a/config/agents/example.agent.json +++ b/config/agents/example.agent.json @@ -33,8 +33,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/memory.agent.json b/config/agents/memory.agent.json index da090cfd6..934774076 100644 --- a/config/agents/memory.agent.json +++ b/config/agents/memory.agent.json @@ -22,8 +22,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/starknet-rpc.agent.json b/config/agents/starknet-rpc.agent.json index 6f98003b3..d001f6464 100644 --- a/config/agents/starknet-rpc.agent.json +++ b/config/agents/starknet-rpc.agent.json @@ -26,8 +26,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/agents/supervisor.agent.json b/config/agents/supervisor.agent.json index e0a2afc3f..6b4d3a190 100644 --- a/config/agents/supervisor.agent.json +++ b/config/agents/supervisor.agent.json @@ -24,8 +24,8 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "openai", - "model_name": "gpt-4o", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 } diff --git a/config/models/default.models.json b/config/models/default.models.json index 3d7a75e18..179605141 100644 --- a/config/models/default.models.json +++ b/config/models/default.models.json @@ -1,21 +1,21 @@ { "fast": { - "provider": "openai", - "model_name": "gpt-4o-mini", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "description": "Optimized for speed and simple tasks.", "max_input_tokens": 0, "max_output_tokens": 0 }, "smart": { - "provider": "anthropic", - "model_name": "claude-3-5-sonnet-latest", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "description": "Optimized for complex reasoning.", "max_input_tokens": 0, "max_output_tokens": 0 }, "cheap": { - "provider": "openai", - "model_name": "gpt-4o-mini", + "provider": "google-genai", + "model_name": "gemini-2.5-flash", "description": "Good cost-performance balance.", "max_input_tokens": 0, "max_output_tokens": 0 From 6bec715371ffa9ead5fbb9211323661ee009d515 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 14:53:24 +0000 Subject: [PATCH 12/18] version with partial config input http --- .../agent/interfaces/agent.interface.ts | 11 +++++ .../src/common/constant/agents.constants.ts | 46 +++++++++++++++++++ .../src/common/server/dto/agent/config.dto.ts | 2 +- .../src/controllers/agents.controller.ts | 11 +++-- packages/server/src/utils/agents.utils.ts | 29 +++++++++++- 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/packages/core/src/common/agent/interfaces/agent.interface.ts b/packages/core/src/common/agent/interfaces/agent.interface.ts index 47f5b9ada..46031d35d 100644 --- a/packages/core/src/common/agent/interfaces/agent.interface.ts +++ b/packages/core/src/common/agent/interfaces/agent.interface.ts @@ -147,6 +147,17 @@ export namespace AgentConfig { graph: GraphConfig; } + /** + * Input configuration with partial memory, mcp_servers and rag + * Useful for updates where these configurations are optional + */ + export interface InputWithPartialConfig + extends Omit { + memory?: Partial; + mcp_servers?: Record; + rag?: Partial; + } + /** * Input configuration with optional parameters for updates */ diff --git a/packages/core/src/common/constant/agents.constants.ts b/packages/core/src/common/constant/agents.constants.ts index 20332c9ed..6f5753828 100644 --- a/packages/core/src/common/constant/agents.constants.ts +++ b/packages/core/src/common/constant/agents.constants.ts @@ -2,6 +2,7 @@ import { AgentConfig, MemoryStrategy, } from '@common/agent/interfaces/agent.interface.js'; +import { Agent } from 'http'; /** * Agent Selector Configuration @@ -125,3 +126,48 @@ export const supervisorAgentConfig: AgentConfig.Input = { mcp_servers: {}, }; + +export namespace AgentConfigDefaults { + export const graph = { + max_steps: 50, + max_iterations: 50, + max_retries: 3, + execution_timeout_ms: 120000, + max_token_usage: 16000, + model: { + provider: 'gemini-2.5-flash', + model_name: 'gemini', + temperature: 0.7, + max_tokens: 8192, + }, + }; + + export const memory = { + ltm_enabled: true, + strategy: MemoryStrategy.HOLISTIC, + size_limits: { + short_term_memory_size: 10, + max_insert_episodic_size: 20, + max_insert_semantic_size: 20, + max_retrieve_memory_size: 20, + limit_before_summarization: 10000, + }, + thresholds: { + insert_semantic_threshold: 0.8, + insert_episodic_threshold: 0.75, + retrieve_memory_threshold: 0.4, + hitl_threshold: 0.85, + }, + timeouts: { + retrieve_memory_timeout_ms: 20000, + insert_memory_timeout_ms: 10000, + }, + }; + + export const rag = { + enabled: false, + top_k: 0, + }; + + export const mcp_servers = {}; +} diff --git a/packages/core/src/common/server/dto/agent/config.dto.ts b/packages/core/src/common/server/dto/agent/config.dto.ts index 7580f1e08..8a45dcede 100644 --- a/packages/core/src/common/server/dto/agent/config.dto.ts +++ b/packages/core/src/common/server/dto/agent/config.dto.ts @@ -93,7 +93,7 @@ export class AgentDeletesRequestDTO { export class AgentAddRequestDTO { @IsNotEmpty() - agent: AgentConfig.Input; + agent: AgentConfig.InputWithPartialConfig; } export class AgentAvatarResponseDTO { diff --git a/packages/server/src/controllers/agents.controller.ts b/packages/server/src/controllers/agents.controller.ts index 8409db3db..c5b982a51 100644 --- a/packages/server/src/controllers/agents.controller.ts +++ b/packages/server/src/controllers/agents.controller.ts @@ -45,6 +45,7 @@ import { } from '@snakagent/database/queries'; import { supervisorAgentConfig } from '@snakagent/core'; +import { initializeAgentConfigIfMissingParams } from '../utils/agents.utils.js'; export interface AgentAvatarResponseDTO { id: string; @@ -355,11 +356,15 @@ export class AgentsController { ): Promise { logger.info('init_agent called'); const userId = ControllerHelpers.getUserId(req); - - this.supervisorService.validateNotSupervisorAgent(userRequest.agent); + const agentConfigurationNormalized = initializeAgentConfigIfMissingParams( + userRequest.agent + ); + this.supervisorService.validateNotSupervisorAgent( + agentConfigurationNormalized + ); const newAgentConfig = await this.agentFactory.addAgent( - userRequest.agent, + agentConfigurationNormalized, userId ); diff --git a/packages/server/src/utils/agents.utils.ts b/packages/server/src/utils/agents.utils.ts index 7b37e9f5f..fb12e4c02 100644 --- a/packages/server/src/utils/agents.utils.ts +++ b/packages/server/src/utils/agents.utils.ts @@ -1,6 +1,11 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'; import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; -import { ModelConfig, logger } from '@snakagent/core'; +import { + AgentConfig, + AgentConfigDefaults, + ModelConfig, + logger, +} from '@snakagent/core'; const SUPPORTED_GEMINI_MODELS = ['gemini-2.5-flash', 'gemini-2.5-pro']; @@ -46,3 +51,25 @@ export function initializeModels(model: ModelConfig): BaseChatModel | null { return null; } } + +export function initializeAgentConfigIfMissingParams( + agentConfig: AgentConfig.InputWithPartialConfig +): AgentConfig.Input { + try { + if (!agentConfig.mcp_servers) { + agentConfig.mcp_servers = AgentConfigDefaults.mcp_servers; + } + if (!agentConfig.memory) { + agentConfig.memory = AgentConfigDefaults.memory; + } + if (!agentConfig.rag) { + agentConfig.rag = AgentConfigDefaults.rag; + } + if (!agentConfig.graph) { + agentConfig.graph = AgentConfigDefaults.graph; + } + return agentConfig as AgentConfig.Input; + } catch (error) { + throw error; + } +} From aad3d9e463f447226e5d22bf746035a2d47f6a6a Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:02:10 +0000 Subject: [PATCH 13/18] version with the fix of reflactor --- packages/core/package.json | 1 + packages/core/src/index.ts | 2 ++ packages/server/main.ts | 3 +++ packages/server/package.json | 7 +++--- pnpm-lock.yaml | 39 ++++++++++++++++++------------ tests/src/file-test/file-upload.ts | 2 +- 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index cb532ab85..217ca0696 100755 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -36,6 +36,7 @@ "file-type": "^19.0.0", "pg": "^8.13.3", "prom-client": "^15.1.3", + "reflect-metadata": "^0.2.2", "winston": "^3.17.0", "zod": "^3.24.2" }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index cc140afef..bc268c6d8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,3 +1,5 @@ +import 'reflect-metadata'; + export type * from './common/agent/interfaces/agent.interface.js'; export * from './common/agent/interfaces/agent.interface.js'; diff --git a/packages/server/main.ts b/packages/server/main.ts index 1dfa209b7..8a7ea84db 100644 --- a/packages/server/main.ts +++ b/packages/server/main.ts @@ -1,3 +1,6 @@ +// CRITICAL: reflect-metadata must be imported first for class-transformer decorators +import 'pnpm -metadata'; + // CRITICAL: Initialize Guards BEFORE any other imports that might use getGuardValue import './init-guards.js'; diff --git a/packages/server/package.json b/packages/server/package.json index 9ba5011f3..4d2de0789 100755 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -33,8 +33,8 @@ "@nestjs/throttler": "^6.4.0", "@snakagent/agents": "workspace:*", "@snakagent/core": "workspace:*", - "@snakagent/metrics": "workspace:*", "@snakagent/database": "workspace:*", + "@snakagent/metrics": "workspace:*", "@snakagent/workers": "workspace:*", "class-transformer": "^0.5.1", "class-validator": "^0.14.1", @@ -46,9 +46,10 @@ "ioredis": "^5.3.2", "mammoth": "^1.9.1", "pdfjs-dist": "^5.3.31", - "uuid": "^11.1.0", "platform-express": "^0.0.13", - "rxjs": "^7.8.1" + "reflect-metadata": "^0.2.2", + "rxjs": "^7.8.1", + "uuid": "^11.1.0" }, "devDependencies": { "@types/pg": "^8.11.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f98ea29b..7f9499520 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -101,7 +101,7 @@ importers: devDependencies: '@nestjs/cli': specifier: ^10.4.9 - version: 10.4.9 + version: 10.4.9(esbuild@0.25.9) '@nestjs/schematics': specifier: ^10.2.3 version: 10.2.3(chokidar@3.6.0)(typescript@5.9.3) @@ -176,10 +176,10 @@ importers: version: 7.1.4 ts-jest: specifier: ^29.4.4 - version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.8)(ts-node@10.9.2(@types/node@22.18.8)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.9)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.8)(ts-node@10.9.2(@types/node@22.18.8)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.4 - version: 9.5.4(typescript@5.9.3)(webpack@5.97.1) + version: 9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.25.9)) tsconfig-paths: specifier: ^4.2.0 version: 4.2.0 @@ -252,6 +252,9 @@ importers: prom-client: specifier: ^15.1.3 version: 15.1.3 + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 winston: specifier: ^3.17.0 version: 3.17.0 @@ -358,6 +361,9 @@ importers: platform-express: specifier: ^0.0.13 version: 0.0.13(openapi-types@12.1.3)(pg@8.16.3) + reflect-metadata: + specifier: ^0.2.2 + version: 0.2.2 rxjs: specifier: ^7.8.1 version: 7.8.2 @@ -9496,7 +9502,7 @@ snapshots: '@emnapi/runtime': 1.5.0 '@tybys/wasm-util': 0.9.0 - '@nestjs/cli@10.4.9': + '@nestjs/cli@10.4.9(esbuild@0.25.9)': dependencies: '@angular-devkit/core': 17.3.11(chokidar@3.6.0) '@angular-devkit/schematics': 17.3.11(chokidar@3.6.0) @@ -9506,7 +9512,7 @@ snapshots: chokidar: 3.6.0 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1) + fork-ts-checker-webpack-plugin: 9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.25.9)) glob: 10.4.5 inquirer: 8.2.6 node-emoji: 1.11.0 @@ -9515,7 +9521,7 @@ snapshots: tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.7.2 - webpack: 5.97.1 + webpack: 5.97.1(esbuild@0.25.9) webpack-node-externals: 3.0.0 transitivePeerDependencies: - esbuild @@ -12163,7 +12169,7 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1): + fork-ts-checker-webpack-plugin@9.0.2(typescript@5.7.2)(webpack@5.97.1(esbuild@0.25.9)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -12178,7 +12184,7 @@ snapshots: semver: 7.7.3 tapable: 2.3.0 typescript: 5.7.2 - webpack: 5.97.1 + webpack: 5.97.1(esbuild@0.25.9) form-data-encoder@1.7.2: {} @@ -15486,14 +15492,16 @@ snapshots: temp-dir@1.0.0: {} - terser-webpack-plugin@5.3.14(webpack@5.97.1): + terser-webpack-plugin@5.3.14(esbuild@0.25.9)(webpack@5.97.1(esbuild@0.25.9)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.0 - webpack: 5.97.1 + webpack: 5.97.1(esbuild@0.25.9) + optionalDependencies: + esbuild: 0.25.9 terser@5.44.0: dependencies: @@ -15608,7 +15616,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.8)(ts-node@10.9.2(@types/node@22.18.8)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.4))(esbuild@0.25.9)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.18.8)(ts-node@10.9.2(@types/node@22.18.8)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -15626,9 +15634,10 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.28.4) + esbuild: 0.25.9 jest-util: 29.7.0 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.97.1): + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.97.1(esbuild@0.25.9)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 @@ -15636,7 +15645,7 @@ snapshots: semver: 7.7.3 source-map: 0.7.6 typescript: 5.9.3 - webpack: 5.97.1 + webpack: 5.97.1(esbuild@0.25.9) ts-mixer@6.0.4: {} @@ -15978,7 +15987,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.97.1: + webpack@5.97.1(esbuild@0.25.9): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -16000,7 +16009,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(webpack@5.97.1) + terser-webpack-plugin: 5.3.14(esbuild@0.25.9)(webpack@5.97.1(esbuild@0.25.9)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: diff --git a/tests/src/file-test/file-upload.ts b/tests/src/file-test/file-upload.ts index e137bac71..7baf42ca4 100644 --- a/tests/src/file-test/file-upload.ts +++ b/tests/src/file-test/file-upload.ts @@ -23,7 +23,7 @@ async function testFiles() { await testRunner.runTest('Health Check', () => testRunner.client.healthCheck()); const createResult = await testRunner.runTest('Create Agent for File Testing', () => - testRunner.client.createAgent({~ + testRunner.client.createAgent({ agent: { name: 'File Test Agent', group: 'test', From 1f86760bc0bffb5cc9c086c165fe97bf17824e11 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:03:15 +0000 Subject: [PATCH 14/18] fixc metadata --- packages/server/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/main.ts b/packages/server/main.ts index 8a7ea84db..a2a686f3f 100644 --- a/packages/server/main.ts +++ b/packages/server/main.ts @@ -1,5 +1,5 @@ // CRITICAL: reflect-metadata must be imported first for class-transformer decorators -import 'pnpm -metadata'; +import 'pnpm-metadata'; // CRITICAL: Initialize Guards BEFORE any other imports that might use getGuardValue import './init-guards.js'; From 357233ad3236fed1d6f25605c06e9ea0bfde8513 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:04:08 +0000 Subject: [PATCH 15/18] fix --- packages/server/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/main.ts b/packages/server/main.ts index a2a686f3f..f0a5ffd3a 100644 --- a/packages/server/main.ts +++ b/packages/server/main.ts @@ -1,5 +1,5 @@ // CRITICAL: reflect-metadata must be imported first for class-transformer decorators -import 'pnpm-metadata'; +import 'reflect-metadata'; // CRITICAL: Initialize Guards BEFORE any other imports that might use getGuardValue import './init-guards.js'; From 5cd8e1da1cd9a36f95dad50b807cde4d97722785 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:13:39 +0000 Subject: [PATCH 16/18] nortmalize model_provider --- .../core/src/services/agent-validation.service.ts | 4 ++-- packages/database/src/queries/agents/queries.ts | 12 +----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/packages/core/src/services/agent-validation.service.ts b/packages/core/src/services/agent-validation.service.ts index 0e02eea86..f603217c5 100644 --- a/packages/core/src/services/agent-validation.service.ts +++ b/packages/core/src/services/agent-validation.service.ts @@ -275,10 +275,10 @@ export class AgentValidationService { const maxTokens = getGuardValue('agents.graph.model.max_tokens'); // Validate provider - REQUIRED - if (!model.model_provider) { + if (!model.provider) { throw new Error('Model provider is required'); } - if (model.model_provider.toLowerCase() !== allowedProvider.toLowerCase()) { + if (model.provider.toLowerCase() !== allowedProvider.toLowerCase()) { throw new Error( `Invalid model provider. Only '${allowedProvider}' is supported.` ); diff --git a/packages/database/src/queries/agents/queries.ts b/packages/database/src/queries/agents/queries.ts index ac9f089e5..274b2f440 100644 --- a/packages/database/src/queries/agents/queries.ts +++ b/packages/database/src/queries/agents/queries.ts @@ -2,6 +2,7 @@ import { AgentConfig, AgentProfile, McpServerConfig, + ModelConfig, supervisorAgentConfig, } from '@snakagent/core'; import { Postgres } from '../../database.js'; @@ -14,17 +15,6 @@ export namespace agents { id: string; avatar_mime_type: string; } - - /** - * Model configuration data - */ - export interface ModelConfig { - provider: string; - model_name: string; - temperature: number; - max_tokens: number; - } - /** * Prompts data */ From 2e8dd58f94c7852b0b51d12675e467aa5c5ffff6 Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:33:57 +0000 Subject: [PATCH 17/18] updated --- .../core/src/common/constant/agents.constants.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/src/common/constant/agents.constants.ts b/packages/core/src/common/constant/agents.constants.ts index 6f5753828..cb71674bd 100644 --- a/packages/core/src/common/constant/agents.constants.ts +++ b/packages/core/src/common/constant/agents.constants.ts @@ -28,8 +28,8 @@ export const agentSelectorConfig: AgentConfig.Input = { execution_timeout_ms: 30000, max_token_usage: 4000, model: { - provider: 'gemini-2.5-flash', - model_name: 'gemini', + provider: 'gemini', + model_name: 'gemini-2.5-flash', temperature: 0.7, max_tokens: 8192, }, @@ -90,8 +90,8 @@ export const supervisorAgentConfig: AgentConfig.Input = { execution_timeout_ms: 120000, max_token_usage: 16000, model: { - provider: 'gemini-2.5-flash', - model_name: 'gemini', + provider: 'gemini', + model_name: 'gemini-2.5-flash', temperature: 0.7, max_tokens: 8192, }, @@ -135,8 +135,8 @@ export namespace AgentConfigDefaults { execution_timeout_ms: 120000, max_token_usage: 16000, model: { - provider: 'gemini-2.5-flash', - model_name: 'gemini', + provider: 'gemini', + model_name: 'gemini-2.5-flash', temperature: 0.7, max_tokens: 8192, }, From 1663cf3704762891749dc72525ded83b2906445c Mon Sep 17 00:00:00 2001 From: Etienne Duclos Date: Tue, 28 Oct 2025 15:38:09 +0000 Subject: [PATCH 18/18] version with cionfig update --- config/agents/cairo.agent.json | 2 +- config/agents/default.agent.json | 2 +- config/agents/ethereum-rpc.agent.json | 2 +- config/agents/example.agent.json | 2 +- config/agents/memory.agent.json | 2 +- config/agents/starknet-rpc.agent.json | 2 +- config/agents/supervisor.agent.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/config/agents/cairo.agent.json b/config/agents/cairo.agent.json index b4ecb7183..7ac352a78 100644 --- a/config/agents/cairo.agent.json +++ b/config/agents/cairo.agent.json @@ -32,7 +32,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/default.agent.json b/config/agents/default.agent.json index 624116bdf..f28b5c50d 100644 --- a/config/agents/default.agent.json +++ b/config/agents/default.agent.json @@ -40,7 +40,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/ethereum-rpc.agent.json b/config/agents/ethereum-rpc.agent.json index 05f2757ac..b6a7d5d0b 100644 --- a/config/agents/ethereum-rpc.agent.json +++ b/config/agents/ethereum-rpc.agent.json @@ -45,7 +45,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/example.agent.json b/config/agents/example.agent.json index 6e1b22699..4e34b91a3 100644 --- a/config/agents/example.agent.json +++ b/config/agents/example.agent.json @@ -33,7 +33,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/memory.agent.json b/config/agents/memory.agent.json index 934774076..0273b2072 100644 --- a/config/agents/memory.agent.json +++ b/config/agents/memory.agent.json @@ -22,7 +22,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/starknet-rpc.agent.json b/config/agents/starknet-rpc.agent.json index d001f6464..aa7977dd4 100644 --- a/config/agents/starknet-rpc.agent.json +++ b/config/agents/starknet-rpc.agent.json @@ -26,7 +26,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096 diff --git a/config/agents/supervisor.agent.json b/config/agents/supervisor.agent.json index 6b4d3a190..e0a57bc33 100644 --- a/config/agents/supervisor.agent.json +++ b/config/agents/supervisor.agent.json @@ -24,7 +24,7 @@ "execution_timeout_ms": 300000, "max_token_usage": 100000, "model": { - "provider": "google-genai", + "provider": "gemini", "model_name": "gemini-2.5-flash", "temperature": 0.7, "max_tokens": 4096