diff --git a/modus/agents.mdx b/modus/agents.mdx index d9597df2..86b10d7b 100644 --- a/modus/agents.mdx +++ b/modus/agents.mdx @@ -35,41 +35,208 @@ Agents are perfect for: Every agent starts with the essential framework: + + + + `agents.go` + ```go package main import ( - "fmt" - "strings" - "time" - "github.com/hypermodeinc/modus/sdk/go/pkg/agents" - "github.com/hypermodeinc/modus/sdk/go/pkg/models" - "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" + "encoding/json" + "errors" + "fmt" + "math" + "strings" + "time" + + "github.com/hypermodeinc/modus/sdk/go/pkg/agents" + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" ) type IntelligenceAgent struct { - agents.AgentBase - - // The rest of the fields make up the agent's state and can be customized per agent - intelligenceReports []string // Matrix surveillance data - threatLevel float64 // Current threat assessment - lastContact time.Time - currentMission *MissionPhase // Track long-running operations - missionLog []string // Operational progress log + agents.AgentBase + intelligenceReports []string + threatLevel float64 + lastContact time.Time + currentMission *MissionPhase + missionLog []string } +``` + +`types.go` +```go Go Expandable type MissionPhase struct { - Name string - StartTime time.Time - Duration time.Duration - Complete bool + Name string + StartTime time.Time + Duration time.Duration + Complete bool +} + +type MissionStatus struct { + Phase string `json:"phase"` + Progress float64 `json:"progress"` + CurrentTask string `json:"current_task"` + EstimatedTimeRemaining int `json:"estimated_time_remaining"` + IsComplete bool `json:"is_complete"` +} + +type ThreatDetected struct { + ThreatLevel string `json:"threatLevel"` + Confidence float64 `json:"confidence"` + Analysis string `json:"analysis"` +} + +type MissionStarted struct { + MissionName string `json:"missionName"` + Priority string `json:"priority"` + ActivityData int `json:"activityData"` +} + +type SurveillanceProgress struct { + Phase string `json:"phase"` + Progress float64 `json:"progress"` + ReportsProcessed int `json:"reportsProcessed"` + TotalReports int `json:"totalReports"` +} + +type AIAnalysisStarted struct { + ModelName string `json:"modelName"` + ContextSize int `json:"contextSize"` + ReportCount int `json:"reportCount"` +} + +type MissionCompleted struct { + MissionName string `json:"missionName"` + Confidence float64 `json:"confidence"` + ReportsAnalyzed int `json:"reportsAnalyzed"` + Status string `json:"status"` } +``` + + + + +`index.ts` + +```typescript +import { Agent } from "@hypermode/modus-sdk-as" +import { JSON } from "json-as" +import { MissionPhase } from "./types" + +@json +class IntelligenceAgent extends Agent { + private intelReports: string[] = [] + private threatLevel: f64 = 0 + private lastContact: u64 = 0 + private currentMission: MissionPhase | null = null + private missionLog: string[] = [] -func (a *IntelligenceAgent) Name() string { + get name(): string { return "IntelligenceAgent" + } +} +``` + +`types.ts` + +```typescript Expandable +import { AgentEvent } from "@hypermode/modus-sdk-as" + +@json +export class MissionPhase { + name: string = "" + startTime: u64 = 0 + duration: u64 = 0 + complete: bool = false +} + +@json +export class MissionStatus { + phase: string = "" + progress: f64 = 0 + currentTask: string = "" + estimatedTimeRemaining: i32 = 0 + isComplete: bool = false +} + +@json +export class ThreatDetected extends AgentEvent { + eventName: string = "threat_detected" + threatLevel: string = "" + confidence: f64 = 0 + analysis: string = "" + constructor(threatLevel: string, confidence: f64, analysis: string) { + super("threat_detected") + this.threatLevel = threatLevel + this.confidence = confidence + this.analysis = analysis + } +} + +@json +export class MissionStarted extends AgentEvent { + eventName: string = "mission_started" + missionName: string = "" + priority: string = "" + activityData: i32 = 0 + constructor(missionName: string, priority: string, activityData: i32) { + super("mission_started") + this.missionName = missionName + this.priority = priority + this.activityData = activityData + } +} + +@json +export class SurveillanceProgress extends AgentEvent { + eventName: string = "surveillance_progress" + phase: string = "" + progress: f64 = 0 + reportsProcessed: i32 = 0 + totalReports: i32 = 0 + constructor( + phase: string, + progress: f64, + reportsProcessed: i32, + totalReports: i32, + ) { + super("surveillance_progress") + this.phase = phase + this.progress = progress + this.reportsProcessed = reportsProcessed + this.totalReports = totalReports + } +} + +@json +export class MissionCompleted extends AgentEvent { + eventName: string = "mission_completed" + missionName: string = "" + confidence: f64 = 0 + reportsAnalyzed: i32 = 0 + status: string = "" + constructor( + missionName: string, + confidence: f64, + reportsAnalyzed: i32, + status: string, + ) { + super("mission_completed") + this.missionName = missionName + this.confidence = confidence + this.reportsAnalyzed = reportsAnalyzed + this.status = status + } } ``` + + + The agent embeds `agents.AgentBase`, which provides all the infrastructure for state management, secure communications, and persistence. Your app data—intelligence reports, threat assessments, contact logs—lives as fields in @@ -80,24 +247,32 @@ the struct, automatically preserved across all interactions. Agents are created and managed through regular Modus functions that become part of your GraphQL API. These functions handle agent lifecycle operations: -```go -// Register your agent type during initialization + + +```go Go func init() { - agents.Register(&IntelligenceAgent{}) + agents.Register(&IntelligenceAgent{}) } -// Create a new agent instance - this becomes a GraphQL mutation func DeployAgent() (string, error) { - agentInfo, err := agents.Start("IntelligenceAgent") - if err != nil { - return "", err - } + info, err := agents.Start("IntelligenceAgent") + if err != nil { + return "", err + } + return info.Id, nil +} +``` - // Return the agent ID - clients must store this to communicate with the agent - return agentInfo.Id, nil +```typescript AssemblyScript +agents.register() + +export function deployAgent(): string { + return agents.start("IntelligenceAgent").id } ``` + + When you call this function through GraphQL, it returns a unique agent ID: ```graphql @@ -111,7 +286,7 @@ Response: ```json { "data": { - "deployAgent": "agent_neo_001" + "deployAgent": "d1qn141d6u7qu64mesjg" } } ``` @@ -134,41 +309,54 @@ continues operating exactly where it left off. Once created, you communicate with agents using their unique ID. Create functions that send messages to specific agent instances: -```go -func ImportActivity(agentId string, activityData string) (string, error) { - result, err := agents.SendMessage( - agentId, - "matrix_surveillance", - agents.WithData(activityData), - ) - if err != nil { - return "", err - } - if result == nil { - return "", fmt.Errorf("no response from agent") - } - return *result, nil + + +```go Go +func ImportActivity(agentId, activityData string) (string, error) { + res, err := agents.SendMessage(agentId, "matrix_surveillance", agents.WithData(activityData)) + if err != nil { + return "", err + } + if res == nil { + return "", errors.New("no response from agent") + } + return *res, nil } func GetThreatStatus(agentId string) (string, error) { - result, err := agents.SendMessage(agentId, "threat_assessment") - if err != nil { - return "", err - } - if result == nil { - return "", fmt.Errorf("no response from agent") - } - return *result, nil + res, err := agents.SendMessage(agentId, "threat_assessment") + if err != nil { + return "", err + } + if res == nil { + return "", errors.New("no response from agent") + } + return *res, nil +} +``` + +```typescript AssemblyScript +export function importActivity( + agentId: string, + activityData: string, +): string | null { + return agents.sendMessage(agentId, "matrix_surveillance", activityData) +} + +export function getThreatStatus(agentId: string): string | null { + return agents.sendMessage(agentId, "threat_assessment") } ``` + + These functions become GraphQL operations that you can call with your agent's ID: ```graphql mutation { importActivity( - agentId: "agent_neo_001" + agentId: "d1qn141d6u7qu64mesjg" activityData: "Anomalous Agent Smith replication detected in Sector 7" ) } @@ -189,7 +377,7 @@ Response: ```graphql query { - getThreatStatus(agentId: "agent_neo_001") + threatStatus(agentId: "d1qn141d6u7qu64mesjg") } ``` @@ -198,7 +386,7 @@ Response: ```json { "data": { - "getThreatStatus": "Current threat assessment: + "threatStatus": "Current threat assessment: 3 intelligence reports analyzed. Threat level: 0.89. Agent operational in the Matrix." @@ -214,28 +402,50 @@ maintaining persistent memory of every interaction. Agents process requests through their message handling system: -```go -func (a *IntelligenceAgent) OnReceiveMessage( - msgName string, - data string, -) (*string, error) { - switch msgName { - case "matrix_surveillance": - return a.analyzeMatrixActivity(data) - case "background_reconnaissance": - return a.performBackgroundRecon(data) - case "threat_assessment": - return a.getThreatAssessment() - case "get_status": - return a.getOperationalStatus() - case "intelligence_history": - return a.getIntelligenceHistory() - default: - return nil, fmt.Errorf("unrecognized directive: %s", msgName) + + +```go Go +func (a *IntelligenceAgent) OnReceiveMessage(msgName string, data *string) (*string, error) { + switch msgName { + case "matrix_surveillance": + return a.analyzeMatrixActivity(data) + case "background_reconnaissance": + return a.performBackgroundRecon(data) + case "threat_assessment": + return a.getThreatAssessment() + case "get_status": + return a.getOperationalStatus() + default: + return nil, fmt.Errorf("unrecognized directive: %s", msgName) + } +} +``` + +```typescript AssemblyScript focus={3-20} +class IntelligenceAgent extends Agent { + // ... + onReceiveMessage(msgName: string, data: string | null): string | null { + switch (msgName) { + case "matrix_surveillance": + if (!data) return "No data provided" + return this.analyzeMatrixActivity(data) + case "background_reconnaissance": + if (!data) return "No data provided" + this.performBackgroundRecon(data) + return null + case "threat_assessment": + return this.getThreatAssessment() + case "get_status": + return this.getOperationalStatus() + default: + return `Unrecognized directive: ${msgName}` } + } } ``` + + Each message type triggers specific operations, with all data automatically maintained in the agent's persistent memory. @@ -244,68 +454,107 @@ maintained in the agent's persistent memory. Here's how agents handle operations while maintaining persistent state and using AI models for analysis: -```go -func (a *IntelligenceAgent) analyzeMatrixActivity(data string) (*string, error) { - // Store new intelligence in persistent memory - a.intelligenceReports = append(a.intelligenceReports, *data) - a.lastContact = time.Now() - - // Build context from all accumulated intelligence - accumulatedReports := strings.Join(a.intelligenceReports, "\n") - - // AI analysis using complete operational history - model, err := models.GetModel[openai.ChatModel]("analyst-model") - if err != nil { - return nil, err - } + + +```go Go +func (a *IntelligenceAgent) analyzeMatrixActivity(data *string) (*string, error) { + a.IntelReports = append(a.IntelReports, *data) + a.LastContact = time.Now() + + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return nil, err + } + + input, err := model.CreateInput( + openai.NewSystemMessage( + "You are a resistance operative in the Matrix. + Analyze patterns from accumulated surveillance reports and provide threat assessment. + Respond in no more than 3 concise sentences." + ), + openai.NewUserMessage(fmt.Sprintf("All Matrix Intelligence:\n%s\n\nProvide threat assessment:", strings.Join(a.IntelReports, "\n"))), + ) + if err != nil { + return nil, err + } - systemPrompt := `You are a resistance operative in the Matrix. - Analyze patterns from accumulated surveillance reports - and provide threat assessment for anomalous Agent behavior.` + output, err := model.Invoke(input) + if err != nil { + return nil, err + } + analysis := output.Choices[0].Message.Content - userPrompt := fmt.Sprintf(`All Matrix Intelligence: - %s + a.ThreatLevel = math.Min(1.0, float64(len(a.IntelReports))/10.0) + if s := strings.ToLower(analysis); strings.Contains(s, "critical") || strings.Contains(s, "agent smith") { + a.ThreatLevel = math.Min(a.ThreatLevel+0.2, 1.0) + } - Provide threat assessment:`, - accumulatedReports) + result := fmt.Sprintf("Matrix surveillance complete:\n%s\n\n(Threat level: %.2f based on %d intelligence reports)", analysis, a.ThreatLevel, len(a.IntelReports)) + return &result, nil +} +``` - input, err := model.CreateInput( - openai.NewSystemMessage(systemPrompt), - openai.NewUserMessage(userPrompt), +```typescript AssemblyScript focus={3-54} +class IntelligenceAgent extends Agent { + // ... + private analyzeMatrixActivity(data: string): string | null { + this.publishEvent( + new MissionStarted("Matrix Surveillance Analysis", "HIGH", data.length), ) - if err != nil { - return nil, err - } - output, err := model.Invoke(input) - if err != nil { - return nil, err - } - analysis := output.Choices[0].Message.Content + this.intelReports.push(data) + this.lastContact = Date.now() - // Update threat level based on data volume and AI analysis - a.threatLevel = float64(len(a.intelligenceReports)) / 10.0 - if a.threatLevel > 1.0 { - a.threatLevel = 1.0 - } + this.publishEvent( + new SurveillanceProgress( + "Processing Matrix surveillance data", + 0.3, + this.intelReports.length, + this.intelReports.length, + ), + ) - // Boost threat level for critical AI analysis - if strings.Contains(strings.ToLower(analysis), "critical") || - strings.Contains(strings.ToLower(analysis), "agent smith") { - a.threatLevel = math.Min(a.threatLevel + 0.2, 1.0) + const model = models.getModel("text-generator") + const prompt = ` + You are a resistance operative in the Matrix. + Analyze patterns from accumulated surveillance reports and provide threat assessment. + Respond in no more than 2 concise sentences.` + const content = + "All Matrix Intelligence:\n" + + this.intelReports.join("\n") + + "\n\nProvide threat assessment:" + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(content), + ]) + const analysis = model.invoke(input).choices[0].message.content + + this.threatLevel = Math.min(1, this.intelReports.length / 10) + + if ( + analysis.toLowerCase().includes("critical") || + analysis.toLowerCase().includes("agent smith") + ) { + this.threatLevel = Math.min(this.threatLevel + 0.2, 1) + this.publishEvent(new ThreatDetected("HIGH", this.threatLevel, analysis)) } - result := fmt.Sprintf(`Matrix surveillance complete: - %s + this.publishEvent( + new MissionCompleted( + "Matrix Surveillance Analysis", + this.threatLevel, + this.intelReports.length, + "SUCCESS", + ), + ) - (Threat level: %.2f based on %d intelligence reports)`, - analysis, - a.threatLevel, - len(a.intelligenceReports)) - return &result, nil + return `Matrix surveillance complete:\n${analysis}\n\n(Threat level: ${this.threatLevel} based on ${this.intelReports.length} reports)` + } } ``` + + This demonstrates how agents maintain state across complex operations while using AI models with the full context of accumulated intelligence. @@ -333,93 +582,141 @@ sophisticated pattern recognition impossible with stateless functions. Agents automatically preserve their state through Modus's built-in persistence system: -```go + + +```go Go func (a *IntelligenceAgent) GetState() *string { - reportsData := strings.Join(a.intelligenceReports, "|") - state := fmt.Sprintf("%.2f|%s|%d", - a.threatLevel, - reportsData, - a.lastContact.Unix()) - return &state + state := AgentState{ + ThreatLevel: a.threatLevel, + IntelligenceReports: a.intelligenceReports, + LastContactUnix: a.lastContact.Unix(), + CurrentMission: a.currentMission, + MissionLog: a.missionLog, + } + bytes, err := json.Marshal(state) + if err != nil { + return nil + } + result := string(bytes) + return &result } -func (a *IntelligenceAgent) SetState(data string) { - if data == nil { - return - } +func (a *IntelligenceAgent) SetState(data *string) { + if data == nil { + return + } + state := AgentState{} + if err := json.Unmarshal([]byte(*data), &state); err != nil { + return + } + a.threatLevel = state.ThreatLevel + a.intelligenceReports = state.IntelligenceReports + a.lastContact = time.Unix(state.LastContactUnix, 0) + a.currentMission = state.CurrentMission + a.missionLog = state.MissionLog +} +``` - parts := strings.Split(*data, "|") - if len(parts) >= 3 { - a.threatLevel, _ = strconv.ParseFloat(parts[0], 64) - if parts[1] != "" { - a.intelligenceReports = strings.Split(parts[1], "|") - } - timestamp, _ := strconv.ParseInt(parts[2], 10, 64) - a.lastContact = time.Unix(timestamp, 0) - } +```typescript AssemblyScript focus={3-15} +class IntelligenceAgent extends Agent { + // ... + getState(): string { + return JSON.stringify(this) + } + + setState(data: string | null): void { + if (!data) return + const agent = JSON.parse(data) + this.intelReports = agent.intelReports + this.threatLevel = agent.threatLevel + this.lastContact = agent.lastContact + this.currentMission = agent.currentMission + this.missionLog = agent.missionLog + } } ``` + + ## Agent lifecycle Agents have built-in lifecycle management protocols: -```go -func (a *IntelligenceAgent) OnInitialize() error { - // Called when agent is first created - a.lastContact = time.Now() - a.threatLevel = 0.0 + - fmt.Printf(`Resistance Agent %s awakened - and ready for Matrix surveillance`, a.Id()) - return nil +```go Go +func (a *IntelligenceAgent) OnInitialize() error { + a.lastContact = time.Now(); + a.threatLevel = 0; + fmt.Printf("Agent %s ready\n", a.Id()); + return nil } func (a *IntelligenceAgent) OnResume() error { - // Called when agent reconnects with complete state intact - fmt.Printf(`Agent back online in the Matrix. - %d intelligence reports processed. - Threat level: %.2f`, - len(a.intelligenceReports), - a.threatLevel) - return nil + fmt.Printf("Agent resumed. Reports: %d, Threat Level: %.2f\n", len(a.intelligenceReports), a.threatLevel); + return nil } func (a *IntelligenceAgent) OnSuspend() error { - // Called before agent goes offline - return nil + return nil } func (a *IntelligenceAgent) OnTerminate() error { - // Called before final shutdown - fmt.Printf(`Agent %s extracted from Matrix. - Intelligence archive preserved.`, a.Id()) - return nil + fmt.Printf("Agent %s terminated. Archive saved.\n", a.Id()); + return nil +} +``` + +```typescript AssemblyScript focus={3-19} +class IntelligenceAgent extends Agent { + // ... + onInitialize(): void { + this.lastContact = Date.now() + this.threatLevel = 0 + console.log(`Agent ${this.id} ready`) + } + + onResume(): void { + console.log( + `Agent resumed. Reports: ${this.intelReports.length}, Threat Level: ${this.threatLevel}`, + ) + } + + onSuspend(): void {} + + onTerminate(): void { + console.log(`Agent ${this.id} terminated. Archive saved`) + } } ``` + + ## Asynchronous operations For fire-and-forget operations where you don't need to wait for a response, agents support asynchronous messaging: -```go -func InitiateBackgroundRecon(agentId string, data string) error { - // Send message asynchronously - agent processes in background - err := agents.SendMessageAsync( - agentId, - "background_reconnaissance", - agents.WithData(data), - ) - if err != nil { - return err - } + - // Operation initiated - agent continues processing independently - return nil +```go Go +func InitiateBackgroundRecon(agentId, data string) error { + return agents.SendMessageAsync( + agentId, + "background_reconnaissance", + agents.WithData(data) + ) +} +``` + +```typescript AssemblyScript +export function initiateBackgroundRecon(agentId: string, data: string): void { + agents.sendMessageAsync(agentId, "background_reconnaissance", data) } ``` + + This enables agents to handle long-running operations like: - Background Matrix monitoring with status updates @@ -440,7 +737,7 @@ Monitor your agent's real-time activities using the unified event subscription: ```graphql subscription { - agentEvent(agentId: "agent_neo_001") { + agentEvent(agentId: "d1qn141d6u7qu64mesjg") { name data timestamp @@ -505,120 +802,144 @@ Your agent streams various types of operational events: Agents can broadcast real-time operational intelligence by publishing events during their operations. Use the `PublishEvent` method to emit custom events: -```go + + +```go Go // Custom event types implement the AgentEvent interface type ThreatDetected struct { ThreatLevel string `json:"threatLevel"` Confidence float64 `json:"confidence"` Analysis string `json:"analysis"` } - -func (e ThreatDetected) EventName() string { - return "threat_detected" -} +func (ThreatDetected) EventName() string { return "threat_detected" } // Other event types can be defined similarly... -func (a *IntelligenceAgent) analyzeMatrixActivity( - data string, -) (*string, error) { - // Emit mission start event - err := a.PublishEvent(MissionStarted{ - MissionName: "Matrix Surveillance Analysis", - Priority: "HIGH", - ActivityData: len(*data), - }) - if err != nil { - return nil, err - } +func (a *IntelligenceAgent) analyzeMatrixActivity(data *string) (*string, error) { + a.PublishEvent(MissionStarted{ + MissionName: "Matrix Surveillance Analysis", + Priority: "HIGH", + ActivityData: len(*data), + }) + + a.intelligenceReports = append(a.intelligenceReports, *data) + a.lastContact = time.Now() + + a.PublishEvent(SurveillanceProgress{ + Phase: "Processing Matrix surveillance data", + Progress: 0.3, + ReportsProcessed: len(a.intelligenceReports), + TotalReports: len(a.intelligenceReports), + }) + + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return nil, err + } - // Store new intelligence in persistent memory - a.intelligenceReports = append(a.intelligenceReports, *data) - a.lastContact = time.Now() + a.PublishEvent(AIAnalysisStarted{ + ModelName: "text-generator", + ContextSize: len(strings.Join(a.intelligenceReports, "\n")), + ReportCount: len(a.intelligenceReports), + }) - // Emit progress update - a.PublishEvent(SurveillanceProgress{ - ReportsProcessed: len(a.intelligenceReports), - Phase: "Processing Matrix surveillance data", - Progress: 0.3, - }) + input, err := model.CreateInput( + openai.NewSystemMessage("You are a resistance operative in the Matrix. Analyze patterns from accumulated surveillance reports and provide threat assessment."), + openai.NewUserMessage(fmt.Sprintf("All Matrix Intelligence:\n%s\n\nProvide threat assessment:", strings.Join(a.intelligenceReports, "\n"))), + ) + if err != nil { + return nil, err + } - // Build context from all accumulated intelligence - accumulatedReports := strings.Join(a.intelligenceReports, "\n") + output, err := model.Invoke(input) + if err != nil { + return nil, err + } - // AI analysis using complete operational history - model, err := models.GetModel[openai.ChatModel]("analyst-model") - if err != nil { - return nil, err - } + analysis := output.Choices[0].Message.Content + a.threatLevel = math.Min(1.0, float64(len(a.intelligenceReports))/10.0) - systemPrompt := `You are a resistance operative in the Matrix. - Analyze patterns from accumulated surveillance reports - and provide threat assessment for anomalous Agent behavior.` + if strings.Contains(strings.ToLower(analysis), "critical") || strings.Contains(strings.ToLower(analysis), "agent smith") { + a.threatLevel = math.Min(a.threatLevel+0.2, 1.0) + a.PublishEvent(ThreatDetected{ + ThreatLevel: "HIGH", + Confidence: a.threatLevel, + Analysis: analysis, + }) + } - userPrompt := fmt.Sprintf(`All Matrix Intelligence: - %s + a.PublishEvent(MissionCompleted{ + MissionName: "Matrix Surveillance Analysis", + Confidence: a.threatLevel, + ReportsAnalyzed: len(a.intelligenceReports), + Status: "SUCCESS", + }) - Provide threat assessment:`, - accumulatedReports) + result := fmt.Sprintf("Matrix surveillance complete:\n%s\n\n(Threat level: %.2f based on %d reports)", analysis, a.threatLevel, len(a.intelligenceReports)) + return &result, nil +} +``` - input, err := model.CreateInput( - openai.NewSystemMessage(systemPrompt), - openai.NewUserMessage(userPrompt), +```typescript AssemblyScript focus={3-54} +class IntelligenceAgent extends Agent { + // ... + private analyzeMatrixActivity(data: string): string | null { + this.publishEvent( + new MissionStarted("Matrix Surveillance Analysis", "HIGH", data.length), ) - if err != nil { - return nil, err - } - - // Emit AI processing event - a.PublishEvent(AIAnalysisStarted{ - ModelName: "analyst-model", - ContextSize: len(accumulatedReports), - ReportCount: len(a.intelligenceReports), - }) - output, err := model.Invoke(input) - if err != nil { - return nil, err - } - analysis := output.Choices[0].Message.Content + this.intelReports.push(data) + this.lastContact = Date.now() - // Update threat level based on data volume and AI analysis - a.threatLevel = float64(len(a.intelligenceReports)) / 10.0 - if a.threatLevel > 1.0 { - a.threatLevel = 1.0 - } + this.publishEvent( + new SurveillanceProgress( + "Processing Matrix surveillance data", + 0.3, + this.intelReports.length, + this.intelReports.length, + ), + ) - // Check for Agent threats and emit alerts - if strings.Contains(strings.ToLower(analysis), "critical") || - strings.Contains(strings.ToLower(analysis), "agent smith") { - a.threatLevel = math.Min(a.threatLevel + 0.2, 1.0) - a.PublishEvent(ThreatDetected{ - ThreatLevel: "HIGH", - Confidence: a.threatLevel, - Analysis: analysis, - }) + const model = models.getModel("text-generator") + const prompt = `You are a resistance operative in the Matrix. + Analyze patterns from accumulated surveillance reports and provide threat assessment. + Respond in no more than 2 concise sentences.` + const content = + "All Matrix Intelligence:\n" + + this.intelReports.join("\n") + + "\n\nProvide threat assessment:" + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(content), + ]) + const analysis = model.invoke(input).choices[0].message.content + + this.threatLevel = Math.min(1, this.intelReports.length / 10) + + if ( + analysis.toLowerCase().includes("critical") || + analysis.toLowerCase().includes("agent smith") + ) { + this.threatLevel = Math.min(this.threatLevel + 0.2, 1) + this.publishEvent(new ThreatDetected("HIGH", this.threatLevel, analysis)) } - // Emit mission completion - a.PublishEvent(MissionCompleted{ - MissionName: "Matrix Surveillance Analysis", - Confidence: a.threatLevel, - ReportsAnalyzed: len(a.intelligenceReports), - Status: "SUCCESS", - }) - - result := fmt.Sprintf(`Matrix surveillance complete: - %s + this.publishEvent( + new MissionCompleted( + "Matrix Surveillance Analysis", + this.threatLevel, + this.intelReports.length, + "SUCCESS", + ), + ) - (Threat level: %.2f based on %d intelligence reports)`, - analysis, - a.threatLevel, - len(a.intelligenceReports)) - return &result, nil + return `Matrix surveillance complete:\n${analysis}\n\n(Threat level: ${this.threatLevel} based on ${this.intelReports.length} reports)` + } } ``` + + ### Event-driven operational patterns This streaming capability enables sophisticated real-time operational patterns: @@ -655,29 +976,26 @@ Example with curl: curl -N -H "accept: text/event-stream" \ -H "content-type: application/json" \ -X POST http://localhost:8080/graphql \ - -d '{"query":"subscription { agentEvent(agentId: \"agent_neo_001\") { name data timestamp } }"}' + -d '{"query":"subscription { agentEvent(agentId: \"d1qn141d6u7qu64mesjg\") { name data timestamp } }"}' ``` ## Monitoring ongoing operations You can also poll agent status directly through dedicated functions: -```go -func CheckMissionProgress(agentId string) (*MissionStatus, error) { - result, err := agents.SendMessage(agentId, "get_status") - if err != nil { - return nil, err - } - if result == nil { - return nil, fmt.Errorf("no response from agent") - } + - var status MissionStatus - err = json.Unmarshal([]byte(*result), &status) - if err != nil { - return nil, err - } - return &status, nil +```go Go +func CheckMissionProgress(agentId string) (*MissionStatus, error) { + res, err := agents.SendMessage(agentId, "get_status") + if err != nil || res == nil { + return nil, err + } + var status MissionStatus + if err := json.Unmarshal([]byte(*res), &status); err != nil { + return nil, err + } + return &status, nil } type MissionStatus struct { @@ -689,42 +1007,88 @@ type MissionStatus struct { } ``` +```typescript AssemblyScript +export function checkMissionProgress(agentId: string): MissionStatus | null { + const res = agents.sendMessage(agentId, "get_status") + if (!res) return null + return JSON.parse(res) +} +``` + + + The agent tracks its operational status using the mission state we defined earlier: -```go + + +```go Go func (a *IntelligenceAgent) getOperationalStatus() (*string, error) { - var status MissionStatus - - if a.currentMission == nil { - status = MissionStatus{ - Phase: "Standby", - Progress: 1.0, - CurrentTask: "Awaiting mission directives in the Matrix", - IsComplete: true, - } - } else { - // Calculate progress based on mission log entries - progress := float64(len(a.missionLog)) / 4.0 // 4 phases expected - if progress > 1.0 { progress = 1.0 } - - status = MissionStatus{ - Phase: a.currentMission.Name, - Progress: progress, - CurrentTask: a.missionLog[len(a.missionLog)-1], // Latest entry - IsComplete: a.currentMission.Complete, - } + status := MissionStatus{ + Phase: "Standby", + Progress: 1.0, + CurrentTask: "Awaiting mission directives in the Matrix", + IsComplete: true, + } + + if a.currentMission != nil && len(a.missionLog) > 0 { + progress := float64(len(a.missionLog)) / 4.0 + if progress > 1 { + progress = 1 + } + status = MissionStatus{ + Phase: a.currentMission.Name, + Progress: progress, + CurrentTask: a.missionLog[len(a.missionLog)-1], + IsComplete: a.currentMission.Complete, } + } + + jsonData, err := json.Marshal(status) + if err != nil { + return nil, err + } + result := string(jsonData) + return &result, nil +} +``` - statusJson, err := json.Marshal(status) - if err != nil { - return nil, err +```typescript AssemblyScript focus={3-30} +class IntelligenceAgent extends Agent { + // ... + private getOperationalStatus(): string { + const status = new MissionStatus() + + if (this.currentMission && this.missionLog.length > 0) { + let progress = this.missionLog.length / 4.0 + if (progress > 1.0) progress = 1.0 + + const now = Date.now() + const endTime = + this.currentMission!.startTime + this.currentMission!.duration + const remaining = endTime > u64(now) ? endTime - now : 0 + const estimatedSeconds = (remaining / 1000) + + status.phase = this.currentMission!.name + status.progress = progress + status.currentTask = this.missionLog[this.missionLog.length - 1] + status.isComplete = this.currentMission!.complete + status.estimatedTimeRemaining = estimatedSeconds + } else { + status.phase = "Standby" + status.progress = 1.0 + status.currentTask = "Awaiting mission directives in the Matrix" + status.isComplete = true + status.estimatedTimeRemaining = 0 } - result := string(statusJson) - return &result, nil + + return JSON.stringify(status) + } } ``` + + Your client can either poll this status endpoint via GraphQL or subscribe to real-time events for instant updates: diff --git a/modus/data-fetching.mdx b/modus/data-fetching.mdx index c715b6df..2d6c4ed2 100644 --- a/modus/data-fetching.mdx +++ b/modus/data-fetching.mdx @@ -157,9 +157,9 @@ export function getPerson(name: string): Person { const vars = new dgraph.Variables() vars.set("$name", name) - const resp = dgraph.execute( + const resp = dgraph.executeQuery( connection, - new dgraph.Request(new dgraph.Query(statement, vars)), + new dgraph.Query(statement, vars), ) const persons = JSON.parse(resp.Json).persons return persons[0] @@ -468,7 +468,7 @@ class Person { } @json class GetPersonResponse { - getPerson: Person | null + getPerson: Person | null = null } export function getPerson(name: string): Person | null { diff --git a/modus/first-modus-agent.mdx b/modus/first-modus-agent.mdx index 795f7615..126a7b07 100644 --- a/modus/first-modus-agent.mdx +++ b/modus/first-modus-agent.mdx @@ -103,104 +103,162 @@ AssemblyScript if you prefer. For AssemblyScript usage, refer to the Create a function that fetches data from an external API and uses AI for analysis: - - Create `intelligence.go`: - - ```go intelligence.go - package main - - import ( - "errors" - "fmt" - "strings" - - "github.com/hypermodeinc/modus/sdk/go/pkg/http" - "github.com/hypermodeinc/modus/sdk/go/pkg/models" - "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" - ) - - type IntelReport struct { - Quote string `json:"quote"` - Author string `json:"author"` - Analysis string `json:"analysis,omitempty"` - } + + + Create `intelligence.go`: + + ```go intelligence.go + package main - const modelName = "text-generator" + import ( + "errors" + "fmt" + "strings" - // Fetch a random quote and provide AI analysis - func GatherIntelligence() (*IntelReport, error) { - request := http.NewRequest("https://zenquotes.io/api/random") + "github.com/hypermodeinc/modus/sdk/go/pkg/http" + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" + ) - response, err := http.Fetch(request) - if err != nil { - return nil, err + type IntelReport struct { + Quote string `json:"q"` + Author string `json:"a"` + Analysis string `json:"analysis,omitempty"` } - if !response.Ok() { - return nil, fmt.Errorf("request failed: %d %s", response.Status, response.StatusText) + + const modelName = "text-generator" + + // Fetch a random quote and provide AI analysis + func GatherIntelligence() (*IntelReport, error) { + request := http.NewRequest("https://zenquotes.io/api/random") + + response, err := http.Fetch(request) + if err != nil { + return nil, err + } + if !response.Ok() { + return nil, fmt.Errorf("request failed: %d %s", response.Status, response.StatusText) + } + + // Parse the API response + var quotes []IntelReport + response.JSON("es) + if len(quotes) == 0 { + return nil, errors.New("no data received") + } + + // Get the quote + intel := quotes[0] + + // Generate AI analysis + analysis, err := analyzeIntelligence(intel.Quote, intel.Author) + if err != nil { + fmt.Printf("AI analysis failed for %s: %v\n", intel.Author, err) + intel.Analysis = "Analysis unavailable" + } else { + intel.Analysis = analysis + } + + return &intel, nil } - // Parse the API response - var quotes []IntelReport - response.JSON("es) - if len(quotes) == 0 { - return nil, errors.New("no data received") + // Use AI to analyze the quote + func analyzeIntelligence(quote, author string) (string, error) { + model, err := models.GetModel[openai.ChatModel](modelName) + if err != nil { + return "", err + } + + prompt := `You are an analyst. + Provide a brief insight that captures the core meaning + and practical application of this wisdom in 1-2 sentences.` + content := fmt.Sprintf("Quote: \"%s\" - %s", quote, author) + + input, err := model.CreateInput( + openai.NewSystemMessage(prompt), + openai.NewUserMessage(content), + ) + if err != nil { + return "", err + } + + input.Temperature = 0.7 + + output, err := model.Invoke(input) + if err != nil { + return "", err + } + + return strings.TrimSpace(output.Choices[0].Message.Content), nil } + ``` + + + Modify `index.ts` - // Get the quote - intel := quotes[0] + ```ts + import { http, models } from "@hypermode/modus-sdk-as"; + import { OpenAIChatModel, SystemMessage, UserMessage } from "@hypermode/modus-sdk-as/models/openai/chat"; - // Generate AI analysis - analysis, err := analyzeIntelligence(intel.Quote, intel.Author) - if err != nil { - fmt.Printf("AI analysis failed for %s: %v\n", intel.Author, err) - intel.Analysis = "Analysis unavailable" - } else { - intel.Analysis = analysis - } + export function sayHello(name: string | null = null): string { + return `Hello, ${name || "World"}!`; + } - return &intel, nil + @json + class IntelReport { + @alias("q") + quote!: string; + @alias("a") + author!: string; + analysis!: string; } - // Use AI to analyze the quote - func analyzeIntelligence(quote, author string) (string, error) { - model, err := models.GetModel[openai.ChatModel](modelName) - if err != nil { - return "", err - } + const modelName = "text-generator"; + + export function gatherIntelligence(): IntelReport { + const response = http.fetch("https://zenquotes.io/api/random"); + + if (response.status !== 200) + throw new Error("Request failed with status: " + response.status.toString() + " " + response.statusText); + + const quotes = response.json(); + + if (!quotes.length) + throw new Error("No data recieved"); + + const quote = quotes[0]; + const analysis = analyzeIntelligence(quote.quote, quote.author); + + quote.analysis = analysis; - prompt := `You are an analyst. + return quote; + } + + function analyzeIntelligence(quote: string, author: string): string { + const model = models.getModel(modelName); + + const prompt = `You are an analyst. Provide a brief insight that captures the core meaning - and practical application of this wisdom in 1-2 sentences.` - content := fmt.Sprintf("Quote: \"%s\" - %s", quote, author) + and practical application of this wisdom in 1-2 sentences.`; + const content = "Quote: " + quote + " - " + author; - input, err := model.CreateInput( - openai.NewSystemMessage(prompt), - openai.NewUserMessage(content), - ) - if err != nil { - return "", err - } + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(content) + ]); - input.Temperature = 0.7 + input.temperature = 0.7; - output, err := model.Invoke(input) - if err != nil { - return "", err - } + const output = model.invoke(input); - return strings.TrimSpace(output.Choices[0].Message.Content), nil + return output.choices[0].message.content.trim(); } ``` - + + - Restart your development server: - - ```sh - modus dev - ``` - Modus automatically generates a GraphQL API from your functions. Since your function is named `GatherIntelligence()`, it becomes a GraphQL query field called `gatherIntelligence`. @@ -208,13 +266,13 @@ AssemblyScript if you prefer. For AssemblyScript usage, refer to the The explorer is fully GraphQL-compatible, so you can issue this query: ```graphql - query { - gatherIntelligence { - quote - author - analysis + query { + gatherIntelligence { + quote + author + analysis + } } - } ``` You'll receive a response like: diff --git a/modus/functions.mdx b/modus/functions.mdx index 8ad1e3dd..e56c1ec3 100644 --- a/modus/functions.mdx +++ b/modus/functions.mdx @@ -43,14 +43,26 @@ GraphQL API. Your functions become either **queries** (for data retrieval) or Most functions become GraphQL queries—perfect for fetching and processing data: -```go + + +```go Go // This function becomes a GraphQL query func GatherThreatIntelligence(source string) (*ThreatReport, error) { - // Data gathering and processing operation - return fetchThreatData(source) + // Data gathering and processing operation + return fetchThreatData(source) +} +``` + +```ts AssemblyScript +// This function becomes a GraphQL query +export function gatherThreatIntelligence(source: string): ThreatReport { + // Data gathering and processing operation + return fetchThreatData(source) } ``` + + Your functions are now accessible via GraphQL: ```graphql @@ -82,7 +94,9 @@ query { Functions that modify data automatically become GraphQL mutations. Modus detects these by their operation prefixes: -```go + + +```go Go // This becomes a GraphQL mutation func CreateSecurityAlert(data AlertInput) (*SecurityAlert, error) { // Create new security alert @@ -90,6 +104,16 @@ func CreateSecurityAlert(data AlertInput) (*SecurityAlert, error) { } ``` +```ts AssemblyScript +// This becomes a GraphQL mutation +export function createSecurityAlert(data: AlertInput): SecurityAlert { + // Create new security alert + return deploySecurityAlert(data) +} +``` + + + Now you can execute data modifications: ```graphql @@ -130,11 +154,14 @@ automatically become mutations. Here's a complete example that demonstrates how functions integrate external APIs with AI models for intelligent data processing: -```go + + +```go Go package main import ( "fmt" + "net/url" "strings" "github.com/hypermodeinc/modus/sdk/go/pkg/http" "github.com/hypermodeinc/modus/sdk/go/pkg/models" @@ -152,10 +179,11 @@ const modelName = "text-generator" // Function: Gather weather data and provide tactical analysis func GatherWeatherIntelligence(city string) (*WeatherIntel, error) { + city = url.QueryEscape(city) // Fetch weather data from OpenWeatherMap API url := fmt.Sprintf( - "https://api.openweathermap.org/data/2.5/weather?q=%s&appid={{API_KEY}}&units=metric", - city, + "https://api.openweathermap.org/data/2.5/weather?q=%s&units=metric", + city, ) response, err := http.Fetch(url) @@ -244,6 +272,102 @@ func analyzeTacticalConditions(city string, temp float64, conditions string) (st } ``` +```ts AssemblyScript +import { http, models } from "@hypermode/modus-sdk-as" +import { + OpenAIChatModel, + SystemMessage, + UserMessage, +} from "@hypermode/modus-sdk-as/models/openai/chat" + +@json +class WeatherIntel { + city!: string + temperature!: f64 + conditions!: string + analysis!: string +} + +@json +class WeatherData { + name!: string + main!: MainWeatherData + weather!: WeatherDescription[] +} + +@json +class MainWeatherData { + temp!: f64 +} + +@json +class WeatherDescription { + description!: string +} + +const modelName = "text-generator" + +export function gatherWeatherIntelligence(city: string): WeatherIntel { + city = encodeURIComponent(city) + const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric` + const response = http.fetch(url) + + if (!response.ok) + throw new Error( + "Weather data retrieval failed: " + + response.status.toString() + + " " + + response.statusText, + ) + + const weatherData = response.json() + + let conditions = "unknown" + + if (weatherData.weather.length) + conditions = weatherData.weather[0].description + + const analysis = analyzeTacticalConditions( + weatherData.name, + weatherData.main.temp, + conditions, + ) + + return { + city: weatherData.name, + temperature: weatherData.main.temp, + conditions, + analysis, + } +} + +function analyzeTacticalConditions( + city: string, + temperature: f64, + conditions: string, +): string { + const model = models.getModel(modelName) + + const prompt = `You are a tactical analyst evaluating weather conditions for field operations. + Provide a brief tactical assessment of how these weather conditions might impact + outdoor activities, visibility, and operational considerations in 1-2 sentences.` + + const content = `Location: ${city}, Temperatire: ${temperature}, Conditions: ${conditions}` + + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(content), + ]) + input.temperature = 0.7 + + const output = model.invoke(input) + + return output.choices[0].message.content.trim() +} +``` + + + This function automatically becomes available as a GraphQL query: ```graphql diff --git a/modus/knowledge-graphs.mdx b/modus/knowledge-graphs.mdx index 6f6613a3..b7ac934d 100644 --- a/modus/knowledge-graphs.mdx +++ b/modus/knowledge-graphs.mdx @@ -83,71 +83,149 @@ behavior in the simulated reality. The system needs to: When new Agent activity is detected in the Matrix, you need to process it quickly. This is perfect for a stateless function: -```go + + +```go Go +package main + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/hypermodeinc/modus/sdk/go/pkg/dgraph" + "github.com/hypermodeinc/modus/sdk/go/pkg/models" + "github.com/hypermodeinc/modus/sdk/go/pkg/models/openai" +) + type AgentSighting struct { - SightingID string `json:"sighting_id"` - AgentName string `json:"agent_name"` - Location string `json:"location"` - Behavior string `json:"behavior"` - ThreatLevel int `json:"threat_level"` - Timestamp string `json:"timestamp"` -} - -func ImportAgentSighting(sighting AgentSighting) (*string, error) { - // AI-powered analysis of the Agent behavior - analysis, err := analyzeAgentWithAI(sighting.Behavior) - if err != nil { - return nil, err - } + SightingID string `json:"sighting_id"` + AgentName string `json:"agent_name"` + Location string `json:"location"` + Behavior string `json:"behavior"` + ThreatLevel int `json:"threat_level"` + Timestamp string `json:"timestamp"` + AiAnalysis *string `json:"ai_analysis,omitempty"` +} - // Store in knowledge graph - builds organizational knowledge - mutation := dgraph.NewMutation().WithSetJson(fmt.Sprintf(`{ - "dgraph.type": "AgentSighting", - "sighting_id": "%s", - "agent_name": "%s", - "location": "%s", - "behavior": "%s", - "threat_level": %d, - "timestamp": "%s", - "ai_analysis": "%s" - }`, sighting.SightingID, sighting.AgentName, sighting.Location, - sighting.Behavior, sighting.ThreatLevel, sighting.Timestamp, analysis)) - - err = dgraph.ExecuteMutations("dgraph", mutation) - if err != nil { - return nil, err - } +type ThreatAnalysisResponse struct { + SightingCount int `json:"sighting_count"` + ActiveAgents []string `json:"active_agents"` + ThreatAssessment string `json:"threat_assessment"` + Recommendations []string `json:"recommendations"` +} - result := fmt.Sprintf("Agent sighting processed: %s", sighting.SightingID) - return &result, nil +type SightingsData struct { + Sightings []AgentSighting `json:"sightings"` +} + +func ImportAgentSighting(s AgentSighting) (*string, error) { + analysis, err := analyzeAgentWithAI(s.Behavior) + if err != nil { + return nil, err + } + s.AiAnalysis = &analysis + + data, err := json.Marshal(s) + if err != nil { + return nil, err + } + + mutation := dgraph.NewMutation().WithSetJson(string(data)) + if _, err := dgraph.ExecuteMutations("dgraph", mutation); err != nil { + return nil, err + } + + res := fmt.Sprintf("Agent sighting processed: %s", s.SightingID) + return &res, nil } func analyzeAgentWithAI(behavior string) (string, error) { - model, err := models.GetModel[openai.ChatModel]("text-generator") - if err != nil { - return "", err - } + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return "", err + } - prompt := `Analyze this Agent behavior pattern and assess threat level, - behavioral changes, and tactical implications for resistance operations.` + prompt := "Analyze this Agent behavior pattern and assess threat level, behavioral changes, and tactical implications for resistance operations. Respond with no more than 3 short sentences." + input, err := model.CreateInput( + openai.NewSystemMessage(prompt), + openai.NewUserMessage(behavior), + ) + if err != nil { + return "", err + } + input.Temperature = 0.3 - input, err := model.CreateInput( - openai.NewSystemMessage(prompt), - openai.NewUserMessage(behavior), - ) - if err != nil { - return "", err - } - input.Temperature = 0.3 + out, err := model.Invoke(input) + if err != nil { + return "", err + } + return strings.TrimSpace(out.Choices[0].Message.Content), nil +} +``` - output, err := model.Invoke(input) - if err != nil { - return "", err - } - return strings.TrimSpace(output.Choices[0].Message.Content), nil +```typescript AssemblyScript +import { Agent, agents, dgraph, models } from "@hypermode/modus-sdk-as" +import { + OpenAIChatModel, + SystemMessage, + UserMessage, +} from "@hypermode/modus-sdk-as/models/openai/chat" +import { JSON } from "json-as" + +@json +class AgentSighting { + sighting_id!: string + agent_name!: string + location!: string + behavior!: string + timestamp!: string + @omitnull() + ai_analysis: string | null = null +} + +@json +class ThreatAnalysisResponse { + sightings_count!: i32 + active_agents!: string[] + threat_assessment!: string + reccommendations!: string[] +} + +@json +class SightingsData { + sightings!: AgentSighting[] +} + +export function importAgentSighting(s: AgentSighting): string { + const analysis = analyzeAgentWithAI(s.behavior) + s.ai_analysis = analysis + + const mutation = new dgraph.Mutation(JSON.stringify(s)) + dgraph.executeMutations("dgraph", mutation) + + return "Agent sighting processed " + s.sighting_id +} + +function analyzeAgentWithAI(behavior: string): string { + const model = models.getModel("text-generator") + + const prompt = + "Analyze this Agent behavior pattern and assess threat level, behavioral changes, and tactical implications for resistance operations. Respond with no more than 3 short sentences." + + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(behavior), + ]) + input.temperature = 0.3 + + const out = model.invoke(input) + return out.choices[0].message.content.trim() } ``` + + Now let's deploy this data import function and test it: ```graphql @@ -182,90 +260,177 @@ mutation { Now that we've got the Agent sighting data in our knowledge graph, let's analyze the broader threat landscape: -```go -type ThreatAnalysisResponse struct { - SightingCount int `json:"sighting_count"` - ActiveAgents []string `json:"active_agents"` - ThreatAssessment string `json:"threat_assessment"` - Recommendations []string `json:"recommendations"` + + +```go Go +func AnalyzeAgentPatterns(since string) (*ThreatAnalysisResponse, error) { + query := dgraph.NewQuery(` + query analyzeAgents($since: string) { + sightings(func: ge(timestamp, $since)) { + sighting_id + agent_name + location + behavior + threat_level + timestamp + ai_analysis + } + } + `).WithVariable("$since", since) + + resp, err := dgraph.ExecuteQuery("dgraph", query) + if err != nil { + return nil, err + } + + var data SightingsData + if err := json.Unmarshal([]byte(resp.Json), &data); err != nil { + return nil, err + } + + assessment, err := generateThreatAssessment(data.Sightings) + if err != nil { + return nil, err + } + + return &ThreatAnalysisResponse{ + SightingCount: len(data.Sightings), + ActiveAgents: extractActiveAgents(data.Sightings), + ThreatAssessment: assessment, + Recommendations: generateRecommendations(len(data.Sightings)), + }, nil } -func AnalyzeAgentPatterns(timeRange string) (*ThreatAnalysisResponse, error) { - // Query organizational knowledge - traverses relationships - query := dgraph.NewQuery(` - query analyzeAgents($since: string) { - sightings(func: ge(timestamp, $since)) { - agent_name - behavior - threat_level - ai_analysis - } - } - `).WithVariable("$since", timeRange) +func generateThreatAssessment(sightings []AgentSighting) (string, error) { + model, err := models.GetModel[openai.ChatModel]("text-generator") + if err != nil { + return "", err + } + + prompt := "Based on these Agent sightings, provide a strategic threat assessment focusing on behavioral patterns and risks to resistance operations. Respond with no more than 2 short sentences." + data, err := json.Marshal(sightings) + if err != nil { + return "", err + } - response, err := dgraph.ExecuteQuery("dgraph", query) - if err != nil { - return nil, err + input, err := model.CreateInput( + openai.NewSystemMessage(prompt), + openai.NewUserMessage("Agent surveillance data: "+string(data)), + ) + if err != nil { + return "", err + } + input.Temperature = 0.4 + + out, err := model.Invoke(input) + if err != nil { + return "", err + } + + return strings.TrimSpace(out.Choices[0].Message.Content), nil +} + +func extractActiveAgents(sightings []AgentSighting) []string { + seen := map[string]struct{}{} + var agents []string + for _, s := range sightings { + if _, ok := seen[s.AgentName]; !ok { + seen[s.AgentName] = struct{}{} + agents = append(agents, s.AgentName) } + } + return agents +} - // Parse and extract threat data - var data SightingsData - err = json.Unmarshal([]byte(response.Json), &data) - if err != nil { - return nil, err +func generateRecommendations(count int) []string { + switch { + case count > 5: + return []string{ + "Implement additional security measures.", + "Enhance threat detection capabilities.", + "Implement enhanced access control.", } + case count > 2: + return []string{ + "Enhance threat detection capabilities.", + "Implement enhanced access control.", + } + default: + return []string{"Implement additional security measures."} + } +} +``` - // Generate strategic assessment using AI with graph context - assessment, err := generateThreatAssessment(data.Sightings) - if err != nil { - return nil, err +```typescript AssemblyScript +export function analyzeAgentPatterns(since: string): ThreatAnalysisResponse { + const query = new dgraph.Query(` + query analyzeAgents($since: string) { + sightings(func: ge(timestamp, $since)) { + sighting_id + agent_name + location + behavior + threat_level + timestamp + ai_analysis + } } + `).withVariable("since", since) + + const resp = dgraph.executeQuery("dgraph", query) - return &ThreatAnalysisResponse{ - SightingCount: len(data.Sightings), - ActiveAgents: extractActiveAgents(data.Sightings), - ThreatAssessment: assessment, - Recommendations: generateRecommendations(len(data.Sightings)), - }, nil + const data = JSON.parse(resp.Json) + + const assessment = generateThreatAssessment(data.sightings) + + return { + sightings_count: data.sightings.length, + active_agents: extractActiveAgents(data.sightings), + threat_assessment: assessment, + reccommendations: generateRecommendations(data.sightings.length), + } } -func generateThreatAssessment(sightings interface{}) (string, error) { - model, err := models.GetModel[openai.ChatModel]("text-generator") - if err != nil { - return "", err - } +function generateThreatAssessment(sightings: AgentSighting[]): string { + const model = models.getModel("text-generator") - prompt := `Based on these Agent sightings, provide a strategic threat - assessment focusing on behavioral patterns and risks to - resistance operations.` + const prompt = + "Based on these Agent sightings, provide a strategic threat assessment focusing on behavioral patterns and risks to resistance operations. Respond with no more than 2 short sentences." - sightingsJson, err := json.Marshal(sightings) - if err != nil { - return "", err - } + const input = model.createInput([ + new SystemMessage(prompt), + new UserMessage(JSON.stringify(sightings)), + ]) + input.temperature = 0.4 - input, err := model.CreateInput( - openai.NewSystemMessage(prompt), - openai.NewUserMessage(fmt.Sprintf("Agent surveillance data: %s", - string(sightingsJson))), - ) - if err != nil { - return "", err - } - input.Temperature = 0.4 + const out = model.invoke(input) + return out.choices[0].message.content.trim() +} - output, err := model.Invoke(input) - if err != nil { - return "", err - } - return strings.TrimSpace(output.Choices[0].Message.Content), nil +function extractActiveAgents(sightings: AgentSighting[]): string[] { + return sightings.map((s) => s.agent_name) } -// Helper functions for data extraction and recommendations -func extractActiveAgents(sightings []AgentSightingData) []string { /* ... */ } -func generateRecommendations(count int) []string { /* ... */ } +function generateRecommendations(count: i32): string[] { + if (count > 5) { + return [ + "Implement additional security measures.", + "Enhance threat detection capabilities.", + "Implement enhanced access control.", + ] + } else if (count > 2) { + return [ + "Enhance threat detection capabilities.", + "Implement enhanced access control.", + ] + } else { + return ["Implement additional security measures."] + } +} ``` + + Let's query our surveillance data: ```graphql @@ -306,42 +471,46 @@ Now let's enhance our system to automatically coordinate surveillance when new data arrives. We'll deploy persistent surveillance agents and upgrade our import function to trigger them: -```go + + +```go Go +import ( + "time" + "github.com/hypermodeinc/modus/sdk/go/pkg/agents" +) + type SurveillanceAgent struct { - agents.AgentBase - MonitoredSectors []string `json:"monitored_sectors"` - SightingsTracked int `json:"sightings_tracked"` - RecentActivities []string `json:"recent_activities"` - LastSweepTime time.Time `json:"last_sweep_time"` + agents.AgentBase + MonitoredSectors []string `json:"monitored_sectors"` + SightingsTracked int `json:"sightings_tracked"` + RecentActivities []string `json:"recent_activities"` + LastSweepTime time.Time `json:"last_sweep_time"` } func (s *SurveillanceAgent) Name() string { - return "SurveillanceAgent" + return "SurveillanceAgent" } func (s *SurveillanceAgent) OnInitialize() error { - s.MonitoredSectors = []string{ - "Downtown Loop", "Megacity Financial", "Industrial District"} - s.SightingsTracked = 0 - s.RecentActivities = []string{} - s.LastSweepTime = time.Now() - return nil -} - -func (s *SurveillanceAgent) OnReceiveMessage( - msgName string, data string) (*string, error) { - switch msgName { - case "continuous_surveillance": - return s.processNewIntelligence() - case "get_status": - return s.getOperationalStatus() - } - return nil, fmt.Errorf("unrecognized directive: %s", msgName) + s.MonitoredSectors = []string{"Downtown Loop", "Megacity Financial", "Industrial District"} + s.SightingsTracked = 0 + s.RecentActivities = []string{} + s.LastSweepTime = time.Now() + return nil +} + +func (s *SurveillanceAgent) OnReceiveMessage(msgName string, data *string) (*string, error) { + switch msgName { + case "continuous_surveillance": + return s.processNewIntelligence() + case "get_status": + return s.getOperationalStatus() + } + return nil, fmt.Errorf("unrecognized directive: %s", msgName) } func (s *SurveillanceAgent) processNewIntelligence() (*string, error) { - // Query knowledge graph for latest data since last sweep - query := dgraph.NewQuery(` + query := dgraph.NewQuery(` query getRecentSightings($since: string) { sightings(func: ge(timestamp, $since)) { agent_name @@ -351,107 +520,219 @@ func (s *SurveillanceAgent) processNewIntelligence() (*string, error) { } `).WithVariable("$since", s.LastSweepTime.Format(time.RFC3339)) - _, err := dgraph.ExecuteQuery("dgraph", query) - if err != nil { - return nil, err - } - - // Update agent's surveillance state - s.LastSweepTime = time.Now() - s.SightingsTracked += 1 + _, err := dgraph.ExecuteQuery("dgraph", query) + if err != nil { + return nil, err + } - activity := fmt.Sprintf("Auto surveillance at %s", - s.LastSweepTime.Format("15:04:05")) - s.RecentActivities = append(s.RecentActivities, activity) + s.LastSweepTime = time.Now() + s.SightingsTracked++ + activity := fmt.Sprintf("Auto surveillance at %s", s.LastSweepTime.Format("15:04:05")) + s.RecentActivities = append(s.RecentActivities, activity) - // Keep only last 3 activities - if len(s.RecentActivities) > 3 { - s.RecentActivities = s.RecentActivities[1:] - } + if len(s.RecentActivities) > 3 { + s.RecentActivities = s.RecentActivities[1:] + } - result := fmt.Sprintf(`Data processed automatically. - Tracking %d sightings. Matrix integrity: COMPROMISED`, - s.SightingsTracked) - return &result, nil + result := fmt.Sprintf("Data processed automatically.\nTracking %d sightings. Matrix integrity: COMPROMISED", s.SightingsTracked) + return &result, nil } func (s *SurveillanceAgent) getOperationalStatus() (*string, error) { - status := fmt.Sprintf(`Surveillance Agent Status: + status := fmt.Sprintf(`Surveillance Agent Status: - Operational: Active - Monitoring %d sectors: %s - Last sweep: %s - Tracking %d ongoing sightings - Recent activities: %s`, - len(s.MonitoredSectors), - strings.Join(s.MonitoredSectors, ", "), - s.LastSweepTime.Format("2006-01-02 15:04:05"), - s.SightingsTracked, - strings.Join(s.RecentActivities, ", ")) - return &status, nil + len(s.MonitoredSectors), + strings.Join(s.MonitoredSectors, ", "), + s.LastSweepTime.Format("2006-01-02 15:04:05"), + s.SightingsTracked, + strings.Join(s.RecentActivities, ", "), + ) + return &status, nil } -func init() { agents.Register(&SurveillanceAgent{}) } +func (s *SurveillanceAgent) GetState() *string { return nil } +func (s *SurveillanceAgent) SetState(state *string) {} + +func init() { + agents.Register(&SurveillanceAgent{}) +} func DeploySurveillanceAgent() (string, error) { - agentInfo, err := agents.Start("SurveillanceAgent") - if err != nil { - return "", err - } - return agentInfo.Id, nil + agentInfo, err := agents.Start("SurveillanceAgent") + if err != nil { + return "", err + } + return agentInfo.Name + " - " + agentInfo.Id, nil } func GetSurveillanceStatus(agentId string) (string, error) { - result, err := agents.SendMessage(agentId, "get_status") - if err != nil { - return "", err + result, err := agents.SendMessage(agentId, "get_status") + if err != nil { + return "", err + } + if result == nil { + return "", fmt.Errorf("no response from agent") + } + return *result, nil +} +``` + +```typescript AssemblyScript +class SurveillanceAgent extends Agent { + private monitoredSectors: string[] = [] + private sightingsTracked: i32 = 0 + private recentActivities: string[] = [] + private lastSweepTime: Date = new Date(0) + + get name(): string { + return "SurveillanceAgent" + } + + onInitialize(): void { + this.monitoredSectors = [ + "Downtown Loop", + "Megacity Financial", + "Industrial District", + ] + this.lastSweepTime = new Date(Date.now()) + } + + onReceiveMessage(msgName: string, data: string | null): string { + if (msgName === "continuous_surveillance") { + return this.processNewIntelligence() + } else if (msgName === "get_status") { + return this.getOperationalStatus() } - if result == nil { - return "", fmt.Errorf("no response from agent") + throw new Error("Unrecognized directive: " + msgName) + } + + private processNewIntelligence(): string { + const query = new dgraph.Query(` + query getRecentSightings($since: string) { + sightings(func: ge(timestamp, $since)) { + agent_name + threat_level + location + } + } + `).withVariable("since", this.lastSweepTime.toISOString()) + + dgraph.executeQuery("dgraph", query) + + this.lastSweepTime = new Date(Date.now()) + this.sightingsTracked++ + + const activity = "Auto surveillance at " + this.lastSweepTime.toTimeString() + this.recentActivities.push(activity) + + if (this.recentActivities.length > 3) { + this.recentActivities.shift() } - return *result, nil + + return `Data processed automatically.\nTracking ${this.sightingsTracked} sightings. Matrix integrity: COMPROMISED` + } + + private getOperationalStatus(): string { + return `Surveillance Agent Status: +- Operational: Active +- Monitoring ${this.monitoredSectors.length.toString()} sectors: ${this.monitoredSectors.join(", ")} +- Last sweep: ${this.lastSweepTime.toISOString().replace("T", " ").slice(0, 19)} +- Tracking ${this.sightingsTracked.toString()} ongoing sightings +- Recent activities: ${this.recentActivities.join(", ")}` + } + + getState(): string { + return "" + // return JSON.stringify(this); + } + + setState(data: string | null): void { + // if (!data) return; + // const state = JSON.parse(data); + // this.monitoredSectors = state.monitoredSectors; + // this.sightingsTracked = state.sightingsTracked; + // this.recentActivities = state.recentActivities; + // this.lastSweepTime = state.lastSweepTime; + } +} + +agents.register() + +export function deploySurveillanceAgent(): string { + const agentInfo = agents.start("SurveillanceAgent") + return `${agentInfo.name} - ${agentInfo.id}` +} + +export function getSurveillanceStatus(agentId: string): string { + const result = agents.sendMessage(agentId, "get_status") + if (result == null) { + throw new Error("No response from agent") + } + return result } ``` + + Now let's enhance our original import function to automatically trigger surveillance: -```go -func ImportAgentSighting(sighting AgentSighting) (*string, error) { - // AI-powered analysis of the Agent behavior - analysis, err := analyzeAgentWithAI(sighting.Behavior) - if err != nil { - return nil, err - } + - // Store in knowledge graph - builds organizational knowledge - mutation := dgraph.NewMutation().WithSetJson(fmt.Sprintf(`{ - "dgraph.type": "AgentSighting", - "sighting_id": "%s", - "agent_name": "%s", - "location": "%s", - "behavior": "%s", - "threat_level": %d, - "timestamp": "%s", - "ai_analysis": "%s" - }`, sighting.SightingID, sighting.AgentName, sighting.Location, - sighting.Behavior, sighting.ThreatLevel, sighting.Timestamp, analysis)) - - err = dgraph.ExecuteMutations("dgraph", mutation) - if err != nil { - return nil, err - } +```go Go +func ImportAgentSighting(s AgentSighting, surveillanceAgentId *string) (*string, error) { + analysis, err := analyzeAgentWithAI(s.Behavior) + if err != nil { + return nil, err + } + s.AiAnalysis = &analysis + + data, err := json.Marshal(s) + if err != nil { + return nil, err + } + + mutation := dgraph.NewMutation().WithSetJson(string(data)) + if _, err := dgraph.ExecuteMutations("dgraph", mutation); err != nil { + return nil, err + } - // Automatically trigger surveillance via async message - err = agents.SendMessageAsync("agent_neo_001", "continuous_surveillance") - if err != nil { - return nil, err + if surveillanceAgentId != nil { + if err := agents.SendMessageAsync(*surveillanceAgentId, "continuous_surveillance"); err != nil { + return nil, err } + } - result := fmt.Sprintf("Agent sighting processed: %s", sighting.SightingID) - return &result, nil + res := fmt.Sprintf("Agent sighting processed: %s", s.SightingID) + return &res, nil } ``` +```typescript AssemblyScript +export function importAgentSighting( + s: AgentSighting, + surveilanceAgentId: string | null = null, +): string { + const analysis = analyzeAgentWithAI(s.behavior) + s.ai_analysis = analysis + + const mutation = new dgraph.Mutation(JSON.stringify(s)) + dgraph.executeMutations("dgraph", mutation) + + if (surveilanceAgentId) { + agents.sendMessageAsync(surveilanceAgentId, "continuous_surveillance") + } + + return "Agent sighting processed " + s.sighting_id +} +``` + + + Deploy your surveillance agent: ```graphql @@ -507,7 +788,7 @@ messaging. Check the surveillance status: ```graphql query { - getSurveillanceStatus(agentId: "agent_neo_001") + surveillanceStatus(agentId: "agent_neo_001") } ``` @@ -516,14 +797,15 @@ query { ```json { "data": { - "getSurveillanceStatus": "Surveillance Agent Status: - - Operational: Active - - Monitoring 3 sectors: Downtown Loop, - Megacity Financial, Industrial District - - Last sweep: 2025-01-15 15:45:22 - - Tracking 2 ongoing sightings - - Recent activities: Auto surveillance at 14:30:05, - Auto surveillance at 15:45:22" + "surveillanceStatus": + "Surveillance Agent Status: + - Operational: Active + - Monitoring 3 sectors: Downtown Loop, + Megacity Financial, Industrial District + - Last sweep: 2025-01-15 15:45:22 + - Tracking 2 ongoing sightings + - Recent activities: Auto surveillance at 14:30:05, + Auto surveillance at 15:45:22" } } ``` @@ -552,12 +834,13 @@ query { "analyzeAgentPatterns": { "sightingCount": 2, "activeAgents": ["Smith", "Brown"], - "threatAssessment": "Critical escalation detected. Agent Smith's - enhanced replication capabilities combined with - Agent Brown's encryption countermeasures indicates - coordinated Matrix defense upgrade. Systematic - pattern suggests machines adapting to resistance - operations.", + "threatAssessment": + "Critical escalation detected. Agent Smith's + enhanced replication capabilities combined with + Agent Brown's encryption countermeasures indicates + coordinated Matrix defense upgrade. Systematic + pattern suggests machines adapting to resistance + operations.", "recommendations": [ "Emergency extraction protocols", "Activate deep cover cells", diff --git a/modus/model-invoking.mdx b/modus/model-invoking.mdx index aafba0fe..0b67f0a1 100644 --- a/modus/model-invoking.mdx +++ b/modus/model-invoking.mdx @@ -159,7 +159,6 @@ func GenerateText(instruction, prompt string) (string, error) { import { models } from "@hypermode/modus-sdk-as" import { OpenAIChatModel, - ResponseFormat, SystemMessage, UserMessage, } from "@hypermode/modus-sdk-as/models/openai/chat" @@ -225,12 +224,8 @@ func ClassifyText(text string, threshold float32) (string, error) { ```ts AssemblyScript import { models } from "@hypermode/modus-sdk-as" -import { - ClassificationModel, - ClassifierResult, -} from "@hypermode/modus-sdk-as/models/experimental/classification" +import { ClassificationModel } from "@hypermode/modus-sdk-as/models/experimental/classification" -// this model name should match the one defined in the modus.json manifest file const modelName: string = "my-classifier" // this function takes input text and a probability threshold, and returns the @@ -241,12 +236,14 @@ export function classifyText(text: string, threshold: f32): string { const input = model.createInput([text]) const output = model.invoke(input) - const prediction = output.predictions[0] - if (prediction.confidence >= threshold) { - return prediction.label + const predictions = output.predictions + + const prediction = predictions[0] + if (prediction.confidence < threshold) { + return "" } - return "" + return prediction.label } ```