Skip to content

Commit

Permalink
its a final count down
Browse files Browse the repository at this point in the history
  • Loading branch information
yashpokar committed Apr 22, 2024
1 parent 88d121a commit d0cde48
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 24 deletions.
20 changes: 17 additions & 3 deletions apps/api/libs/core/src/ai.service.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Injectable, Logger } from '@nestjs/common'
import { OnEvent } from '@nestjs/event-emitter'
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'
import { MessageEntity } from '@/models/message'
import { END, StateGraph } from '@langchain/langgraph'
import {
AGENT_NODE,
MESSAGE_RECEIVED_EVENT,
MESSAGE_RESPONSE_EVENT,
PLANNER_NODE,
REPLANNER_NODE
} from './constants'
Expand All @@ -13,6 +14,7 @@ import PlannerAgent from './agents/planner'
import RePlannerAgent from './agents/replanner'
import { PlanExecuteState } from './types/agent'
import { Pregel } from '@langchain/langgraph/dist/pregel'
import { MessageResponseEvent } from './types/message'

@Injectable()
export class AIService {
Expand All @@ -22,7 +24,8 @@ export class AIService {
constructor(
private readonly openaiAgent: OpenAIAgent,
private readonly plannerAgent: PlannerAgent,
private readonly replannerAgent: RePlannerAgent
private readonly replannerAgent: RePlannerAgent,
private readonly eventEmitter: EventEmitter2
) {
const workflow = new StateGraph({
channels: {
Expand Down Expand Up @@ -90,7 +93,18 @@ export class AIService {
recursionLimit: 50
}
)) {
this.logger.log(`Event: ${JSON.stringify(event)}`)
this.logger.debug(`Event: `, event)

if (END in event) {
// TODO: refactor this, not a right place
const input: MessageResponseEvent = {
content: event[END].response,
projectId: project.id,
deviceId: project.deviceId
}

this.eventEmitter.emit(MESSAGE_RESPONSE_EVENT, input)
}
}
}
}
1 change: 1 addition & 0 deletions apps/api/libs/core/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const MESSAGE_CREATED_EVENT = 'message::created'
export const MESSAGE_SENT_EVENT = 'message::sent'
export const MESSAGE_RECEIVED_EVENT = 'message::received'
export const MESSAGE_RESPONSE_EVENT = 'message::response'

export const PREVIEW_EVENT = 'preview::event'

Expand Down
14 changes: 14 additions & 0 deletions apps/api/libs/core/src/tools/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { z } from 'zod'
import { Tool } from './tool'
import { Injectable } from '@nestjs/common'
import { chromium } from 'playwright'
import { Browser } from '../models/browser'
import { PREVIEW_EVENT, TOPIC_BROWSER } from '../constants'
import { EventEmitter2 } from '@nestjs/event-emitter'

@Injectable()
class BrowserTool extends Tool {
Expand All @@ -17,6 +20,10 @@ class BrowserTool extends Tool {
)
})

constructor(private readonly eventEmitter: EventEmitter2) {
super()
}

public async execute({ url }) {
this.logger.debug(`testing or previewing URL: ${url}`)

Expand All @@ -31,6 +38,13 @@ class BrowserTool extends Tool {

await browser.close()

const onBrowserPreview: Browser = {
url,
content
}

this.eventEmitter.emit(PREVIEW_EVENT, TOPIC_BROWSER, { onBrowserPreview })

return content
}
}
Expand Down
24 changes: 18 additions & 6 deletions apps/api/libs/core/src/tools/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { Tool } from './tool'
import { Injectable } from '@nestjs/common'
import FileSystemService from '../providers/file-system.service'
import { join } from 'path'
import { EventEmitter2 } from '@nestjs/event-emitter'
import { PREVIEW_EVENT, TOPIC_EDITOR } from '../constants'
import { Editor } from '../models/editor'

@Injectable()
class EditorTool extends Tool {
Expand All @@ -15,15 +18,14 @@ class EditorTool extends Tool {
.enum(['READ', 'WRITE', 'DELETE'])
.describe('The action to perform.'),
filename: z.string().describe('The name of the file to edit or write.'),
path: z
.string()
.describe(
'The relative path from the project root to the file or directory.'
),
path: z.string().describe('The relative path to the file or directory.'),
data: z.string().optional().describe('The data to write to the file.')
})

constructor(private readonly fileSystemService: FileSystemService) {
constructor(
private readonly fileSystemService: FileSystemService,
private readonly eventEmitter: EventEmitter2
) {
super()
}

Expand All @@ -36,6 +38,16 @@ class EditorTool extends Tool {
case 'READ':
return this.fileSystemService.readFile(location)
case 'WRITE':
const onEditorPreview: Editor = {
path,
fileName: filename,
content: data
}

this.eventEmitter.emit(PREVIEW_EVENT, TOPIC_EDITOR, {
onEditorPreview
})

return this.fileSystemService.writeFile(location, data || '')
case 'DELETE':
return this.fileSystemService.deleteFile(location)
Expand Down
20 changes: 17 additions & 3 deletions apps/api/libs/core/src/tools/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { z } from 'zod'
import { Injectable } from '@nestjs/common'
import { Tool } from './tool'
import DockerService from '../providers/docker.service'
import { EventEmitter2 } from '@nestjs/event-emitter'
import { Terminal } from '../models/terminal'
import { PREVIEW_EVENT, TOPIC_TERMINAL } from '../constants'

@Injectable()
class TerminalTool extends Tool {
Expand All @@ -13,16 +16,27 @@ class TerminalTool extends Tool {
command: z.string().describe('The shell command to execute.')
})

constructor(private readonly dockerService: DockerService) {
constructor(
private readonly dockerService: DockerService,
private readonly eventEmitter: EventEmitter2
) {
super()

this.dockerService.initialize()
}

async execute({ command, projectId }) {
this.logger.debug(`Executing shell command: ${command}`)

return this.dockerService.executeCommand(command, projectId)
const output = await this.dockerService.executeCommand(command, projectId)

const onTerminalPreview: Terminal = {
command,
output
}

this.eventEmitter.emit(PREVIEW_EVENT, TOPIC_TERMINAL, { onTerminalPreview })

return output
}
}

Expand Down
5 changes: 5 additions & 0 deletions apps/api/libs/core/src/types/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface MessageResponseEvent {
content: string
projectId: string
deviceId: string
}
8 changes: 8 additions & 0 deletions apps/api/src/resolvers/message.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,20 @@ import {
Resolver,
Subscription
} from '@nestjs/graphql'
import { OnEvent } from '@nestjs/event-emitter'
import { MESSAGE_RECEIVED_EVENT } from '@core/core/constants'
import { MessageResponseEvent } from '@core/core/types/message'

@Resolver()
export class MessageResolver {
private readonly logger = new Logger(MessageResolver.name)
constructor(private readonly service: MessageService) {}

@OnEvent(MESSAGE_RECEIVED_EVENT)
async onMessageReceived(event: MessageResponseEvent): Promise<void> {
return this.service.handleMessageResponse(event)
}

@Mutation(() => Message)
@UseGuards(DeviceIdGuard, ProjectIdGuard)
async createMessage(
Expand Down
22 changes: 21 additions & 1 deletion apps/api/src/services/message.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Injectable, Logger } from '@nestjs/common'
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'
import { InjectRepository } from '@nestjs/typeorm'
import { DataSource, Repository } from 'typeorm'
import { MessageResponseEvent } from '@core/core/types/message'

@Injectable()
export class MessageService {
Expand All @@ -31,9 +32,28 @@ export class MessageService {
private readonly eventEmitter: EventEmitter2
) {}

async handleMessageResponse(event: MessageResponseEvent): Promise<void> {
this.logger.debug(`handling message response`, event)

const message = await this._create(event.projectId, event.deviceId, {
content: event.content,
author: Author.ASSISTANT
})

this.eventEmitter.emit(MESSAGE_SENT_EVENT, message)
}

async create(
ctx: IContext,
input: CreateMessageInput
): Promise<MessageEntity> {
return this._create(ctx.req.projectId, ctx.req.deviceId, input)
}

async _create(
projectId: string,
deviceId: string,
input: CreateMessageInput
): Promise<MessageEntity> {
const queryRunner = this.dataSource.createQueryRunner()

Expand All @@ -44,7 +64,7 @@ export class MessageService {
// Note: not depending on the active project from the database
// in-order to be consistent with the frontend
const project = await queryRunner.manager.findOne(ProjectEntity, {
where: { id: ctx.req.projectId, deviceId: ctx.req.deviceId }
where: { id: projectId, deviceId }
})

if (!project) {
Expand Down
4 changes: 0 additions & 4 deletions apps/api/src/types/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@ export interface IContext {
deviceId: string
projectId: string
}
connection: {
deviceId: string
projectId: string
}
}
9 changes: 7 additions & 2 deletions apps/web/src/components/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React, { useEffect } from 'react'
import CodeEditor, { useMonaco } from '@monaco-editor/react'
import useTheme from '@/hooks/use-theme'
import usePreview from '@/hooks/use-preview'

const Editor: React.FC = () => {
const monaco = useMonaco()
const { isDarkMode } = useTheme()
const { editor: editorPreview } = usePreview()

useEffect(() => {
monaco?.editor.defineTheme('manas-ai', {
Expand All @@ -26,11 +28,14 @@ const Editor: React.FC = () => {
height="100%"
theme="manas-ai"
defaultLanguage="python"
defaultValue={`"""Welcome to ManasAI Editor! 🚀
defaultValue={
editorPreview.content ??
`"""Welcome to ManasAI Editor! 🚀
ManasAI will write a code once starts executing its plan.
"""
`}
`
}
options={{
fontFamily:
'SF Mono, Menlo, Roboto Mono, Ubuntu Mono, Oxygen Mono, monospace',
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/Plan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Plan: React.FC = () => {
</p>
)}

<div className="grid gap-y-1.5 mt-4">
<div className="grid gap-y-1.5 mt-4 overflow-y-auto">
{steps.map((step, idx) => (
<div className="flex items-center text-sm gap-x-2" key={idx}>
<span className={'font-chat leading-relaxed dark:text-zinc-100'}>
Expand Down
9 changes: 8 additions & 1 deletion apps/web/src/components/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import '@xterm/xterm/css/xterm.css'
import useTheme from '@/hooks/use-theme'
import usePreview from '@/hooks/use-preview'

const Shell: React.FC = () => {
const playgroundRef = useRef<HTMLDivElement>(null)
const { isDarkMode } = useTheme()
const { terminal: commands } = usePreview()

useLayoutEffect(() => {
if (!playgroundRef.current) return
Expand Down Expand Up @@ -62,10 +64,15 @@ const Shell: React.FC = () => {
fitAddon.fit()
}, 0)

commands.forEach(({ command, output }) => {
terminal.write(command)
terminal.writeln(`\r\n${output}\r\n $`)
})

return () => {
terminal.dispose()
}
}, [playgroundRef, isDarkMode])
}, [playgroundRef, isDarkMode, commands])

return <div ref={playgroundRef} className="p-2 h-full" />
}
Expand Down
Loading

0 comments on commit d0cde48

Please sign in to comment.