Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin-airi-minecraft/dev/refactor-airi'
Browse files Browse the repository at this point in the history
Co-authored-by: RainbowBird <44741987+luoling8192@users.noreply.github.com>
  • Loading branch information
nekomeowww and luoling8192 committed Feb 14, 2025
2 parents 38ce04f + 699c6ef commit 797fab3
Show file tree
Hide file tree
Showing 63 changed files with 7,436 additions and 0 deletions.
10 changes: 10 additions & 0 deletions services/minecraft/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
OPENAI_API_BASEURL=''
OPENAI_API_KEY=''
OPENAI_MODEL='deepseek-chat'
OPENAI_REASONING_MODEL='deepseek-reasoner'

BOT_USERNAME=''
BOT_HOSTNAME=''
BOT_PORT=''
BOT_PASSWORD=''
BOT_VERSION=''
114 changes: 114 additions & 0 deletions services/minecraft/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# ⛏️ Minecraft agent player for [アイリ (Airi)](https://airi.moeru.ai)

> [!NOTE]
>
> This project is part of the [Project アイリ (Airi)](https://github.com/moeru-ai/airi), we aim to build a LLM-driven VTuber like [Neuro-sama](https://www.youtube.com/@Neurosama) (subscribe if you didn't!) if you are interested in, please do give it a try on [live demo](https://airi.moeru.ai).
An intelligent Minecraft bot powered by LLM. AIRI can understand natural language commands, interact with the world, and assist players in various tasks.

## 🎥 Preview

![demo](./docs/preview.png)

## ✨ Features

- 🗣️ Natural language understanding
- 🏃‍♂️ Advanced pathfinding and navigation
- 🛠️ Block breaking and placing
- 🎯 Combat and PvP capabilities
- 🔄 Auto-reconnect on disconnection
- 📦 Inventory management
- 🤝 Player following and interaction
- 🌍 World exploration and mapping

## 🚀 Getting Started

### 📋 Prerequisites

- 📦 Node.js 22+
- 🔧 pnpm
- 🎮 A Minecraft server (1.20+)

### 🔨 Installation

1. Clone the repository:

```bash
git clone https://github.com/moeru-ai/airi-minecraft.git
cd airi-mc
```

2. Install dependencies:

```bash
pnpm install
```

3. Create a `.env.local` file with your configuration:

```env
OPENAI_API_KEY=your_openai_api_key
OPENAI_API_BASEURL=your_openai_api_baseurl
BOT_USERNAME=your_bot_username
BOT_HOSTNAME=localhost
BOT_PORT=25565
BOT_PASSWORD=optional_password
BOT_VERSION=1.20
```

4. Start the bot:

```bash
pnpm dev
```

## 🎮 Usage

Once the bot is connected, you can interact with it using chat commands in Minecraft. All commands start with `#`.

### Basic Commands

- `#help` - Show available commands
- `#follow` - Make the bot follow you
- `#stop` - Stop the current action
- `#come` - Make the bot come to your location

### Natural Language Commands

You can also give the bot natural language commands, and it will try to understand and execute them. For example:

- "Build a house"
- "Find some diamonds"
- "Help me fight these zombies"
- "Collect wood from nearby trees"

## 🛠️ Development

### Project Structure

```
src/
├── agents/ # AI agent implementations
├── composables/# Reusable composable functions
├── libs/ # Core library code
├── mineflayer/ # Mineflayer plugin implementations
├── prompts/ # AI prompt templates
├── skills/ # Bot skills and actions
└── utils/ # Utility functions
```

### Commands

- `pnpm dev` - Start the bot in development mode
- `pnpm lint` - Run ESLint
- `pnpm typecheck` - Run TypeScript type checking
- `pnpm test` - Run tests

## 🙏 Acknowledgements

- https://github.com/kolbytn/mindcraft

## 🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Binary file added services/minecraft/docs/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions services/minecraft/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "@proj-airi/minecraft-bot",
"type": "module",
"version": "1.0.0",
"packageManager": "pnpm@9.15.5",
"description": "An intelligent Minecraft bot powered by LLM. AIRI can understand natural language commands, interact with the world, and assist players in various tasks.",
"main": "src/main.ts",
"scripts": {
"dev": "dotenvx run -f .env -f .env.local --overload --debug --ignore=MISSING_ENV_FILE -- tsx src/main.ts",
"start": "dotenvx run -f .env -f .env.local --overload --ignore=MISSING_ENV_FILE -- tsx src/main.ts",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit",
"test": "vitest",
"postinstall": "npx simple-git-hooks"
},
"dependencies": {
"@dotenvx/dotenvx": "^1.34.0",
"@guiiai/logg": "^1.0.7",
"@proj-airi/server-sdk": "^0.1.4",
"@typeschema/zod": "^0.14.0",
"awilix": "^12.0.4",
"dotenv": "^16.4.7",
"es-toolkit": "^1.32.0",
"eventemitter3": "^5.0.1",
"minecraft-data": "^3.83.1",
"mineflayer": "^4.26.0",
"mineflayer-armor-manager": "^2.0.1",
"mineflayer-auto-eat": "^5.0.0",
"mineflayer-collectblock": "^1.6.0",
"mineflayer-pathfinder": "^2.4.5",
"mineflayer-pvp": "^1.3.2",
"mineflayer-tool": "^1.2.0",
"neuri": "^0.0.21",
"prismarine-block": "^1.21.0",
"prismarine-entity": "^2.5.0",
"prismarine-item": "^1.16.0",
"prismarine-recipe": "^1.3.1",
"prismarine-viewer": "^1.30.0",
"prismarine-windows": "^2.9.0",
"vec3": "^0.1.10",
"zod": "^3.24.1",
"zod-to-json-schema": "^3.24.1"
},
"devDependencies": {
"@antfu/eslint-config": "^4.1.1",
"eslint": "^9.19.0",
"lint-staged": "^15.4.3",
"simple-git-hooks": "^2.11.1",
"tsx": "^4.19.2",
"typescript": "^5.7.3",
"vitest": "^3.0.4"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
}
40 changes: 40 additions & 0 deletions services/minecraft/src/agents/action/adapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { messages, system, user } from 'neuri/openai'
import { beforeAll, describe, expect, it } from 'vitest'

import { initBot, useBot } from '../../composables/bot'
import { config, initEnv } from '../../composables/config'
import { createNeuriAgent } from '../../composables/neuri'
import { generateSystemBasicPrompt } from '../../libs/llm-agent/prompt'
import { initLogger } from '../../utils/logger'

describe('openAI agent', { timeout: 0 }, () => {
beforeAll(() => {
initLogger()
initEnv()
initBot({ botConfig: config.bot })
})

it('should initialize the agent', async () => {
const { bot } = useBot()
const agent = await createNeuriAgent(bot)

await new Promise<void>((resolve) => {
bot.bot.once('spawn', async () => {
const text = await agent.handle(
messages(
system(generateSystemBasicPrompt('airi')),
user('Hello, who are you?'),
),
async (c) => {
const completion = await c.reroute('query', c.messages, { model: config.openai.model })
return await completion?.firstContent()
},
)

expect(text?.toLowerCase()).toContain('airi')

resolve()
})
})
})
})
84 changes: 84 additions & 0 deletions services/minecraft/src/agents/action/adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import type { Agent } from 'neuri'
import type { Message } from 'neuri/openai'
import type { Mineflayer } from '../../libs/mineflayer'
import type { PlanStep } from '../planning/adapter'

import { agent } from 'neuri'
import { system, user } from 'neuri/openai'

import { BaseLLMHandler } from '../../libs/llm-agent/handler'
import { useLogger } from '../../utils/logger'
import { actionsList } from './tools'

export async function createActionNeuriAgent(mineflayer: Mineflayer): Promise<Agent> {
const logger = useLogger()
logger.log('Initializing action agent')
let actionAgent = agent('action')

Object.values(actionsList).forEach((action) => {
actionAgent = actionAgent.tool(
action.name,
action.schema,
async ({ parameters }) => {
logger.withFields({ name: action.name, parameters }).log('Calling action')
mineflayer.memory.actions.push(action)
const fn = action.perform(mineflayer)
return await fn(...Object.values(parameters))
},
{ description: action.description },
)
})

return actionAgent.build()
}

export class ActionLLMHandler extends BaseLLMHandler {
public async executeStep(step: PlanStep): Promise<string> {
const systemPrompt = this.generateActionSystemPrompt()
const userPrompt = this.generateActionUserPrompt(step)
const messages = [system(systemPrompt), user(userPrompt)]

const result = await this.handleAction(messages)
return result
}

private generateActionSystemPrompt(): string {
return `You are a Minecraft bot action executor. Your task is to execute a given step using available tools.
You have access to various tools that can help you accomplish tasks.
When using a tool:
1. Choose the most appropriate tool for the task
2. Determine the correct parameters based on the context
3. Handle any errors or unexpected situations
Remember to:
- Be precise with tool parameters
- Consider the current state of the bot
- Handle failures gracefully`
}

private generateActionUserPrompt(step: PlanStep): string {
return `Execute this step: ${step.description}
Suggested tool: ${step.tool}
Params: ${JSON.stringify(step.params)}
Please use the appropriate tool with the correct parameters to accomplish this step.
If the suggested tool is not appropriate, you may choose a different one.`
}

public async handleAction(messages: Message[]): Promise<string> {
const result = await this.config.agent.handleStateless(messages, async (context) => {
this.logger.log('Processing action...')
const retryHandler = this.createRetryHandler(
async ctx => (await this.handleCompletion(ctx, 'action', ctx.messages)).content,
)
return await retryHandler(context)
})

if (!result) {
throw new Error('Failed to process action')
}

return result
}
}
Loading

0 comments on commit 797fab3

Please sign in to comment.