File size: 13,058 Bytes
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc826a1
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc826a1
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc826a1
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc826a1
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cc826a1
fbd9d3d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
"""
JSON内容提取插件 MCP工具定义
"""

from app.mcp.decorators import mcp_tool
from pydantic import BaseModel, Field
from typing import Any, List, Optional


class ExtractFromJsonInput(BaseModel):
    """从JSON数据提取content的输入参数"""
    json_data: Any = Field(description="JSON数据对象")
    output_file: Optional[str] = Field(
        default=None,
        description="输出文件路径(可选,不提供则返回格式化文本)"
    )


class ExtractFromJsonOutput(BaseModel):
    """从JSON数据提取content的输出结果"""
    success: bool = Field(description="操作是否成功")
    contents: List[str] = Field(default=[], description="提取的content列表")
    formatted_output: str = Field(default="", description="格式化后的输出")
    count: int = Field(default=0, description="提取的content数量")
    error: Optional[str] = Field(default=None, description="错误信息")


class ExtractFromFileInput(BaseModel):
    """从JSON文件提取content的输入参数"""
    file_path: str = Field(description="JSON文件路径")
    output_file: Optional[str] = Field(
        default=None,
        description="输出文件路径(可选)"
    )


class ExtractFromFileOutput(BaseModel):
    """从JSON文件提取content的输出结果"""
    success: bool = Field(description="操作是否成功")
    file_path: str = Field(description="源文件路径")
    contents: List[str] = Field(default=[], description="提取的content列表")
    formatted_output: str = Field(default="", description="格式化后的输出")
    count: int = Field(default=0, description="提取的content数量")
    error: Optional[str] = Field(default=None, description="错误信息")


class ExtractFromDirectoryInput(BaseModel):
    """从目录批量提取content的输入参数"""
    dir_path: str = Field(description="包含JSON文件的目录路径")
    output_file: Optional[str] = Field(
        default=None,
        description="输出文件路径(可选)"
    )


class ExtractFromDirectoryOutput(BaseModel):
    """从目录批量提取content的输出结果"""
    success: bool = Field(description="操作是否成功")
    dir_path: str = Field(description="源目录路径")
    file_count: int = Field(default=0, description="处理的文件数量")
    result: str = Field(default="", description="提取结果")
    error: Optional[str] = Field(default=None, description="错误信息")


class ConversationMessage(BaseModel):
    """对话消息结构"""
    role: str = Field(description="消息角色(system/user/assistant)")
    content: str = Field(description="消息内容")


class ExtractConversationFromJsonInput(BaseModel):
    """从JSON数据提取对话的输入参数"""
    json_data: Any = Field(description="JSON数据对象")


class ExtractConversationFromJsonOutput(BaseModel):
    """从JSON数据提取对话的输出结果"""
    success: bool = Field(description="操作是否成功")
    is_conversation: bool = Field(description="是否为对话格式")
    messages: List[ConversationMessage] = Field(default=[], description="对话消息列表")
    formatted_output: str = Field(default="", description="格式化后的对话记录")
    count: int = Field(default=0, description="消息数量")
    error: Optional[str] = Field(default=None, description="错误信息")


class ExtractConversationFromFileInput(BaseModel):
    """从JSON文件提取对话的输入参数"""
    file_path: str = Field(description="JSON文件路径")


class ExtractConversationFromFileOutput(BaseModel):
    """从JSON文件提取对话的输出结果"""
    success: bool = Field(description="操作是否成功")
    file_path: str = Field(description="源文件路径")
    is_conversation: bool = Field(description="是否为对话格式")
    messages: List[ConversationMessage] = Field(default=[], description="对话消息列表")
    formatted_output: str = Field(default="", description="格式化后的对话记录")
    count: int = Field(default=0, description="消息数量")
    error: Optional[str] = Field(default=None, description="错误信息")


# 全局核心逻辑实例
_core = None


def _get_core():
    """获取核心逻辑实例"""
    global _core
    if _core is None:
        from .core import JsonContentExtractorCore
        _core = JsonContentExtractorCore()
    return _core


@mcp_tool(
    name="json-content",
    title="从JSON数据提取content",
    description="从JSON数据对象中递归提取所有content字段的内容。如果是OpenAI对话格式,会自动识别并按对话格式输出。",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
    }
)
async def extract_from_json(params: ExtractFromJsonInput) -> ExtractFromJsonOutput:
    """
    从JSON数据中提取content字段

    支持递归提取嵌套的content字段,包括处理OpenAI API格式的content数组。
    如果是OpenAI对话格式(包含messages数组),会自动识别并按对话格式输出。

    Args:
        params: 包含JSON数据

    Returns:
        ExtractFromJsonOutput: 提取的内容
    """
    core = _get_core()

    try:
        # 检测是否为对话格式
        if core._is_conversation_format(params.json_data):
            formatted_output = core._format_conversation(params.json_data)
            messages = core._extract_conversation(params.json_data)
            contents = [msg["content"] for msg in messages]
        else:
            contents = core.extract_content_from_json(params.json_data)

            formatted_contents = []
            for i, content in enumerate(contents, 1):
                formatted = core.format_content(content)
                formatted_contents.append(f"=== Content {i} ===\n{formatted}")

            formatted_output = "\n\n".join(formatted_contents) if formatted_contents else "未找到content内容"

        # 如果指定了输出文件,保存结果
        if params.output_file and contents:
            try:
                with open(params.output_file, 'w', encoding='utf-8') as f:
                    f.write(formatted_output)
            except Exception as e:
                return ExtractFromJsonOutput(
                    success=False,
                    contents=contents,
                    formatted_output=formatted_output,
                    count=len(contents),
                    error=f"保存文件失败: {str(e)}"
                )

        return ExtractFromJsonOutput(
            success=True,
            contents=contents,
            formatted_output=formatted_output,
            count=len(contents)
        )

    except Exception as e:
        return ExtractFromJsonOutput(
            success=False,
            error=str(e)
        )


@mcp_tool(
    name="json-file",
    title="从JSON文件提取content",
    description="从JSON文件中递归提取所有content字段的内容。如果是OpenAI对话格式,会自动识别并按对话格式输出。",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
    }
)
async def extract_from_file(params: ExtractFromFileInput) -> ExtractFromFileOutput:
    """
    从JSON文件中提取content字段

    如果是OpenAI对话格式,会自动识别并按对话格式输出。

    Args:
        params: 包含文件路径

    Returns:
        ExtractFromFileOutput: 提取的内容
    """
    core = _get_core()

    try:
        import os
        if not os.path.exists(params.file_path):
            return ExtractFromFileOutput(
                success=False,
                file_path=params.file_path,
                error=f"文件不存在: {params.file_path}"
            )

        contents = core.extract_content_from_file(params.file_path)
        result = core.process_single_file(params.file_path, output_file=params.output_file)

        return ExtractFromFileOutput(
            success=True,
            file_path=params.file_path,
            contents=contents,
            formatted_output=result,
            count=len(contents)
        )

    except Exception as e:
        return ExtractFromFileOutput(
            success=False,
            file_path=params.file_path,
            error=str(e)
        )


@mcp_tool(
    name="json-dir",
    title="从目录批量提取content",
    description="从目录中所有JSON文件递归提取content字段的内容",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
    }
)
async def extract_from_directory(params: ExtractFromDirectoryInput) -> ExtractFromDirectoryOutput:
    """
    从目录中批量提取content字段

    Args:
        params: 包含目录路径

    Returns:
        ExtractFromDirectoryOutput: 提取的结果
    """
    core = _get_core()

    try:
        import os
        import glob

        if not os.path.exists(params.dir_path):
            return ExtractFromDirectoryOutput(
                success=False,
                dir_path=params.dir_path,
                error=f"目录不存在: {params.dir_path}"
            )

        json_files = glob.glob(os.path.join(params.dir_path, "*.json"))
        result = core.process_directory(params.dir_path, output_file=params.output_file)

        return ExtractFromDirectoryOutput(
            success=True,
            dir_path=params.dir_path,
            file_count=len(json_files),
            result=result
        )

    except Exception as e:
        return ExtractFromDirectoryOutput(
            success=False,
            dir_path=params.dir_path,
            error=str(e)
        )


@mcp_tool(
    name="json-chat",
    title="从JSON数据提取对话",
    description="从JSON数据中提取OpenAI格式的对话消息,返回结构化的对话记录",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
    }
)
async def extract_conversation_from_json(params: ExtractConversationFromJsonInput) -> ExtractConversationFromJsonOutput:
    """
    从JSON数据中提取对话消息

    专门用于处理OpenAI格式的对话JSON,返回结构化的消息列表。
    如果不是对话格式,is_conversation会返回false。

    Args:
        params: 包含JSON数据

    Returns:
        ExtractConversationFromJsonOutput: 结构化的对话消息
    """
    core = _get_core()

    try:
        messages = core.extract_conversation_from_json(params.json_data)
        is_conversation = len(messages) > 0

        if is_conversation:
            formatted_output = core._format_conversation(params.json_data)
        else:
            formatted_output = "该数据不是对话格式"

        return ExtractConversationFromJsonOutput(
            success=True,
            is_conversation=is_conversation,
            messages=[ConversationMessage(**msg) for msg in messages],
            formatted_output=formatted_output,
            count=len(messages)
        )

    except Exception as e:
        return ExtractConversationFromJsonOutput(
            success=False,
            error=str(e)
        )


@mcp_tool(
    name="json-chatfile",
    title="从JSON文件提取对话",
    description="从JSON文件中提取OpenAI格式的对话消息,返回结构化的对话记录",
    annotations={
        "readOnlyHint": True,
        "destructiveHint": False,
    }
)
async def extract_conversation_from_file(params: ExtractConversationFromFileInput) -> ExtractConversationFromFileOutput:
    """
    从JSON文件中提取对话消息

    专门用于处理OpenAI格式的对话JSON,返回结构化的消息列表。
    如果不是对话格式,is_conversation会返回false。

    Args:
        params: 包含文件路径

    Returns:
        ExtractConversationFromFileOutput: 结构化的对话消息
    """
    core = _get_core()

    try:
        import os
        if not os.path.exists(params.file_path):
            return ExtractConversationFromFileOutput(
                success=False,
                file_path=params.file_path,
                error=f"文件不存在: {params.file_path}"
            )

        messages = core.extract_conversation_from_file(params.file_path)
        is_conversation = len(messages) > 0

        if is_conversation:
            import json
            with open(params.file_path, 'r', encoding='utf-8') as f:
                json_data = json.load(f)
            formatted_output = core._format_conversation(json_data)
        else:
            formatted_output = "该文件不是对话格式"

        return ExtractConversationFromFileOutput(
            success=True,
            file_path=params.file_path,
            is_conversation=is_conversation,
            messages=[ConversationMessage(**msg) for msg in messages],
            formatted_output=formatted_output,
            count=len(messages)
        )

    except Exception as e:
        return ExtractConversationFromFileOutput(
            success=False,
            file_path=params.file_path,
            error=str(e)
        )