auto-coder-web 0.1.24__py3-none-any.whl → 0.1.26__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- auto_coder_web/auto_coder_runner_wrapper.py +7 -0
- auto_coder_web/common_router/__init__.py +0 -0
- auto_coder_web/common_router/auto_coder_conf_router.py +39 -0
- auto_coder_web/common_router/chat_list_router.py +75 -0
- auto_coder_web/common_router/completions_router.py +53 -0
- auto_coder_web/common_router/file_group_router.py +192 -0
- auto_coder_web/common_router/file_router.py +79 -0
- auto_coder_web/proxy.py +22 -477
- auto_coder_web/routers/chat_router.py +330 -0
- auto_coder_web/routers/coding_router.py +330 -0
- auto_coder_web/routers/config_router.py +199 -0
- auto_coder_web/routers/todo_router.py +463 -20
- auto_coder_web/version.py +1 -1
- auto_coder_web/web/asset-manifest.json +6 -6
- auto_coder_web/web/index.html +1 -1
- auto_coder_web/web/static/css/main.b9764291.css +6 -0
- auto_coder_web/web/static/css/main.b9764291.css.map +1 -0
- auto_coder_web/web/static/js/main.a707a18c.js +3 -0
- auto_coder_web/web/static/js/{main.470202a1.js.LICENSE.txt → main.a707a18c.js.LICENSE.txt} +43 -1
- auto_coder_web/web/static/js/{main.470202a1.js.map → main.a707a18c.js.map} +1 -1
- {auto_coder_web-0.1.24.dist-info → auto_coder_web-0.1.26.dist-info}/METADATA +1 -1
- {auto_coder_web-0.1.24.dist-info → auto_coder_web-0.1.26.dist-info}/RECORD +27 -16
- {auto_coder_web-0.1.24.dist-info → auto_coder_web-0.1.26.dist-info}/top_level.txt +1 -0
- expert_routers/__init__.py +3 -0
- expert_routers/history_router.py +333 -0
- auto_coder_web/web/static/css/main.770925e5.css +0 -6
- auto_coder_web/web/static/css/main.770925e5.css.map +0 -1
- auto_coder_web/web/static/js/main.470202a1.js +0 -3
- {auto_coder_web-0.1.24.dist-info → auto_coder_web-0.1.26.dist-info}/WHEEL +0 -0
- {auto_coder_web-0.1.24.dist-info → auto_coder_web-0.1.26.dist-info}/entry_points.txt +0 -0
auto_coder_web/proxy.py
CHANGED
@@ -23,19 +23,9 @@ from auto_coder_web.file_group import FileGroupManager
|
|
23
23
|
from auto_coder_web.file_manager import get_directory_tree
|
24
24
|
from auto_coder_web.auto_coder_runner import AutoCoderRunner
|
25
25
|
from autocoder.agent.auto_filegroup import AutoFileGroup
|
26
|
-
from .types import (
|
27
|
-
|
28
|
-
|
29
|
-
CompletionItem,
|
30
|
-
CompletionResponse,
|
31
|
-
ChatList,
|
32
|
-
HistoryQuery,
|
33
|
-
ValidationResponse,
|
34
|
-
QueryWithFileNumber,
|
35
|
-
ValidationResponseWithFileNumbers,
|
36
|
-
FileContentResponse,
|
37
|
-
FileChange,
|
38
|
-
CommitDiffResponse,
|
26
|
+
from .types import (
|
27
|
+
ChatList,
|
28
|
+
FileContentResponse,
|
39
29
|
)
|
40
30
|
|
41
31
|
from rich.console import Console
|
@@ -53,8 +43,10 @@ import yaml
|
|
53
43
|
import git
|
54
44
|
import hashlib
|
55
45
|
from datetime import datetime
|
56
|
-
from
|
57
|
-
from .routers import todo_router, settings_router, auto_router, commit_router
|
46
|
+
from auto_coder_web.auto_coder_runner_wrapper import AutoCoderRunnerWrapper
|
47
|
+
from .routers import todo_router, settings_router, auto_router, commit_router, chat_router, coding_router
|
48
|
+
from expert_routers import history_router
|
49
|
+
from .common_router import completions_router, file_router, auto_coder_conf_router, chat_list_router, file_group_router
|
58
50
|
|
59
51
|
|
60
52
|
|
@@ -331,12 +323,9 @@ class ProxyServer:
|
|
331
323
|
|
332
324
|
self.setup_static_files()
|
333
325
|
self.project_path = project_path
|
334
|
-
|
326
|
+
self.auto_coder_runner = AutoCoderRunnerWrapper(project_path, product_mode=product_mode)
|
335
327
|
self.setup_routes()
|
336
328
|
self.client = httpx.AsyncClient()
|
337
|
-
|
338
|
-
self.auto_coder_runner = AutoCoderRunner(project_path, product_mode=product_mode)
|
339
|
-
self.file_group_manager = FileGroupManager(self.auto_coder_runner)
|
340
329
|
|
341
330
|
def setup_middleware(self):
|
342
331
|
self.app.add_middleware(
|
@@ -361,13 +350,23 @@ class ProxyServer:
|
|
361
350
|
|
362
351
|
def setup_routes(self):
|
363
352
|
|
353
|
+
# Store project_path in app state for dependency injection
|
354
|
+
self.app.state.project_path = self.project_path
|
355
|
+
# Store auto_coder_runner in app state for dependency injection
|
356
|
+
self.app.state.auto_coder_runner = self.auto_coder_runner
|
357
|
+
|
364
358
|
self.app.include_router(todo_router.router)
|
365
359
|
self.app.include_router(settings_router.router)
|
366
360
|
self.app.include_router(auto_router.router)
|
367
361
|
self.app.include_router(commit_router.router)
|
368
|
-
|
369
|
-
|
370
|
-
self.app.
|
362
|
+
self.app.include_router(chat_router.router)
|
363
|
+
self.app.include_router(coding_router.router)
|
364
|
+
self.app.include_router(history_router)
|
365
|
+
self.app.include_router(completions_router.router)
|
366
|
+
self.app.include_router(file_router.router)
|
367
|
+
self.app.include_router(auto_coder_conf_router.router)
|
368
|
+
self.app.include_router(chat_list_router.router)
|
369
|
+
self.app.include_router(file_group_router.router)
|
371
370
|
|
372
371
|
@self.app.on_event("shutdown")
|
373
372
|
async def shutdown_event():
|
@@ -378,23 +377,6 @@ class ProxyServer:
|
|
378
377
|
session_id = str(uuid.uuid4())
|
379
378
|
await terminal_manager.handle_websocket(websocket, session_id)
|
380
379
|
|
381
|
-
@self.app.delete("/api/files/{path:path}")
|
382
|
-
async def delete_file(path: str):
|
383
|
-
try:
|
384
|
-
full_path = os.path.join(self.project_path, path)
|
385
|
-
if os.path.exists(full_path):
|
386
|
-
if os.path.isdir(full_path):
|
387
|
-
import shutil
|
388
|
-
shutil.rmtree(full_path)
|
389
|
-
else:
|
390
|
-
os.remove(full_path)
|
391
|
-
return {"message": f"Successfully deleted {path}"}
|
392
|
-
else:
|
393
|
-
raise HTTPException(
|
394
|
-
status_code=404, detail="File not found")
|
395
|
-
except Exception as e:
|
396
|
-
raise HTTPException(status_code=500, detail=str(e))
|
397
|
-
|
398
380
|
@self.app.get("/", response_class=HTMLResponse)
|
399
381
|
async def read_root():
|
400
382
|
if os.path.exists(self.index_html_path):
|
@@ -410,61 +392,10 @@ class ProxyServer:
|
|
410
392
|
def get_project_runner(project_path: str) -> AutoCoderRunner:
|
411
393
|
return self.projects[project_path]
|
412
394
|
|
413
|
-
@self.app.post("/api/file-groups")
|
414
|
-
async def create_file_group(request: Request):
|
415
|
-
data = await request.json()
|
416
|
-
name = data.get("name")
|
417
|
-
description = data.get("description", "")
|
418
|
-
group = await self.file_group_manager.create_group(name, description)
|
419
|
-
return group
|
420
|
-
|
421
|
-
@self.app.post("/api/file-groups/auto")
|
422
|
-
async def auto_create_groups(request: Request):
|
423
|
-
try:
|
424
|
-
data = await request.json()
|
425
|
-
file_size_limit = data.get("file_size_limit", 100)
|
426
|
-
skip_diff = data.get("skip_diff", False)
|
427
|
-
group_num_limit = data.get("group_num_limit", 10)
|
428
|
-
|
429
|
-
# Create AutoFileGroup instance
|
430
|
-
auto_grouper = AutoFileGroup(
|
431
|
-
operate_config_api.get_llm(self.auto_coder_runner.memory),
|
432
|
-
self.project_path,
|
433
|
-
skip_diff=skip_diff,
|
434
|
-
file_size_limit=file_size_limit,
|
435
|
-
group_num_limit=group_num_limit
|
436
|
-
)
|
437
|
-
|
438
|
-
# Get groups
|
439
|
-
groups = auto_grouper.group_files()
|
440
|
-
|
441
|
-
# Create groups using file_group_manager
|
442
|
-
for group in groups:
|
443
|
-
await self.file_group_manager.create_group(
|
444
|
-
name=group.name,
|
445
|
-
description=group.description
|
446
|
-
)
|
447
|
-
# Add files to the group
|
448
|
-
await self.file_group_manager.add_files_to_group(
|
449
|
-
group.name,
|
450
|
-
group.urls
|
451
|
-
)
|
452
|
-
|
453
|
-
return {"status": "success", "message": f"Created {len(groups)} groups"}
|
454
|
-
except Exception as e:
|
455
|
-
raise HTTPException(status_code=500, detail=str(e))
|
456
|
-
|
457
395
|
@self.app.get("/api/os")
|
458
396
|
async def get_os():
|
459
397
|
return {"os": os.name}
|
460
398
|
|
461
|
-
@self.app.post("/api/file-groups/switch")
|
462
|
-
async def switch_file_groups(request: Request):
|
463
|
-
data = await request.json()
|
464
|
-
group_names = data.get("group_names", [])
|
465
|
-
result = await self.file_group_manager.switch_groups(group_names)
|
466
|
-
return result
|
467
|
-
|
468
399
|
@self.app.get("/api/conf/keys")
|
469
400
|
async def get_conf_keys():
|
470
401
|
"""Get all available configuration keys from AutoCoderArgs"""
|
@@ -492,29 +423,6 @@ class ProxyServer:
|
|
492
423
|
})
|
493
424
|
return {"keys": keys}
|
494
425
|
|
495
|
-
@self.app.delete("/api/file-groups/{name}")
|
496
|
-
async def delete_file_group(name: str):
|
497
|
-
await self.file_group_manager.delete_group(name)
|
498
|
-
return {"status": "success"}
|
499
|
-
|
500
|
-
@self.app.post("/api/file-groups/{name}/files")
|
501
|
-
async def add_files_to_group(name: str, request: Request):
|
502
|
-
data = await request.json()
|
503
|
-
files = data.get("files", [])
|
504
|
-
description = data.get("description")
|
505
|
-
if description is not None:
|
506
|
-
group = await self.file_group_manager.update_group_description(name, description)
|
507
|
-
else:
|
508
|
-
group = await self.file_group_manager.add_files_to_group(name, files)
|
509
|
-
return group
|
510
|
-
|
511
|
-
@self.app.delete("/api/file-groups/{name}/files")
|
512
|
-
async def remove_files_from_group(name: str, request: Request):
|
513
|
-
data = await request.json()
|
514
|
-
files = data.get("files", [])
|
515
|
-
group = await self.file_group_manager.remove_files_from_group(name, files)
|
516
|
-
return group
|
517
|
-
|
518
426
|
@self.app.post("/api/revert")
|
519
427
|
async def revert():
|
520
428
|
try:
|
@@ -523,388 +431,25 @@ class ProxyServer:
|
|
523
431
|
except Exception as e:
|
524
432
|
raise HTTPException(status_code=500, detail=str(e))
|
525
433
|
|
526
|
-
@self.app.get("/api/file-groups")
|
527
|
-
async def get_file_groups():
|
528
|
-
groups = await self.file_group_manager.get_groups()
|
529
|
-
return {"groups": groups}
|
530
|
-
|
531
|
-
@self.app.get("/api/files")
|
532
|
-
async def get_files():
|
533
|
-
tree = get_directory_tree(self.project_path)
|
534
|
-
return {"tree": tree}
|
535
|
-
|
536
|
-
@self.app.get("/api/completions/files")
|
537
|
-
async def get_file_completions(name: str = Query(...)):
|
538
|
-
"""获取文件名补全"""
|
539
|
-
matches = self.auto_coder_runner.find_files_in_project([name])
|
540
|
-
completions = []
|
541
|
-
project_root = self.auto_coder_runner.project_path
|
542
|
-
for file_name in matches:
|
543
|
-
path_parts = file_name.split(os.sep)
|
544
|
-
# 只显示最后三层路径,让显示更简洁
|
545
|
-
display_name = os.sep.join(
|
546
|
-
path_parts[-3:]) if len(path_parts) > 3 else file_name
|
547
|
-
relative_path = os.path.relpath(file_name, project_root)
|
548
|
-
|
549
|
-
completions.append(CompletionItem(
|
550
|
-
name=relative_path, # 给补全项一个唯一标识
|
551
|
-
path=relative_path, # 实际用于替换的路径
|
552
|
-
display=display_name, # 显示的简短路径
|
553
|
-
location=relative_path # 完整的相对路径信息
|
554
|
-
))
|
555
|
-
return CompletionResponse(completions=completions)
|
556
|
-
|
557
|
-
@self.app.get("/api/completions/symbols")
|
558
|
-
async def get_symbol_completions(name: str = Query(...)):
|
559
|
-
"""获取符号补全"""
|
560
|
-
symbols = self.auto_coder_runner.get_symbol_list()
|
561
|
-
matches = []
|
562
|
-
|
563
|
-
for symbol in symbols:
|
564
|
-
if name.lower() in symbol.symbol_name.lower():
|
565
|
-
relative_path = os.path.relpath(
|
566
|
-
symbol.file_name, self.project_path)
|
567
|
-
matches.append(CompletionItem(
|
568
|
-
name=symbol.symbol_name,
|
569
|
-
path=f"{symbol.symbol_name} ({relative_path}/{symbol.symbol_type.value})",
|
570
|
-
display=f"{symbol.symbol_name}(location: {relative_path})"
|
571
|
-
))
|
572
|
-
return CompletionResponse(completions=matches)
|
573
|
-
|
574
|
-
@self.app.put("/api/file/{path:path}")
|
575
|
-
async def update_file(path: str, request: Request):
|
576
|
-
try:
|
577
|
-
data = await request.json()
|
578
|
-
content = data.get("content")
|
579
|
-
if content is None:
|
580
|
-
raise HTTPException(
|
581
|
-
status_code=400, detail="Content is required")
|
582
|
-
|
583
|
-
full_path = os.path.join(self.project_path, path)
|
584
|
-
|
585
|
-
# Ensure the directory exists
|
586
|
-
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
587
|
-
|
588
|
-
# Write the file content
|
589
|
-
with open(full_path, 'w', encoding='utf-8') as f:
|
590
|
-
f.write(content)
|
591
|
-
|
592
|
-
return {"message": f"Successfully updated {path}"}
|
593
|
-
except Exception as e:
|
594
|
-
raise HTTPException(status_code=500, detail=str(e))
|
595
|
-
|
596
|
-
@self.app.get("/api/file/{path:path}")
|
597
|
-
async def get_file_content(path: str):
|
598
|
-
from .file_manager import read_file_content
|
599
|
-
content = read_file_content(self.project_path, path)
|
600
|
-
if content is None:
|
601
|
-
raise HTTPException(
|
602
|
-
status_code=404, detail="File not found or cannot be read")
|
603
|
-
|
604
|
-
return {"content": content}
|
605
|
-
|
606
434
|
@self.app.get("/api/active-files")
|
607
435
|
async def get_active_files():
|
608
436
|
"""获取当前活动文件列表"""
|
609
437
|
active_files = self.auto_coder_runner.get_active_files()
|
610
438
|
return active_files
|
611
439
|
|
612
|
-
@self.app.get("/api/conf")
|
613
|
-
async def get_conf():
|
614
|
-
return {"conf": self.auto_coder_runner.get_config()}
|
615
|
-
|
616
|
-
@self.app.post("/api/conf")
|
617
|
-
async def config(request: Request):
|
618
|
-
data = await request.json()
|
619
|
-
try:
|
620
|
-
for key, value in data.items():
|
621
|
-
self.auto_coder_runner.configure(key, str(value))
|
622
|
-
return {"status": "success"}
|
623
|
-
except Exception as e:
|
624
|
-
raise HTTPException(status_code=400, detail=str(e))
|
625
|
-
|
626
|
-
@self.app.delete("/api/conf/{key}")
|
627
|
-
async def delete_config(key: str):
|
628
|
-
try:
|
629
|
-
result = self.auto_coder_runner.drop_config(key)
|
630
|
-
return result
|
631
|
-
except ValueError as e:
|
632
|
-
raise HTTPException(status_code=404, detail=str(e))
|
633
|
-
except Exception as e:
|
634
|
-
raise HTTPException(status_code=400, detail=str(e))
|
635
|
-
|
636
|
-
@self.app.post("/api/coding")
|
637
|
-
async def coding(request: Request):
|
638
|
-
data = await request.json()
|
639
|
-
query = data.get("query", "")
|
640
|
-
if not query:
|
641
|
-
raise HTTPException(
|
642
|
-
status_code=400, detail="Query is required")
|
643
|
-
return await self.auto_coder_runner.coding(query)
|
644
|
-
|
645
|
-
@self.app.post("/api/chat")
|
646
|
-
async def chat(request: Request):
|
647
|
-
data = await request.json()
|
648
|
-
query = data.get("query", "")
|
649
|
-
if not query:
|
650
|
-
raise HTTPException(
|
651
|
-
status_code=400, detail="Query is required")
|
652
|
-
return await self.auto_coder_runner.chat(query)
|
653
|
-
|
654
|
-
@self.app.get("/api/result/{request_id}")
|
655
|
-
async def get_result(request_id: str):
|
656
|
-
result = await self.auto_coder_runner.get_result(request_id)
|
657
|
-
if result is None:
|
658
|
-
raise HTTPException(
|
659
|
-
status_code=404, detail="Result not found or not ready yet")
|
660
|
-
|
661
|
-
v = {"result": result.value, "status": result.status.value}
|
662
|
-
return v
|
663
|
-
|
664
|
-
@self.app.post("/api/event/get")
|
665
|
-
async def get_event(request: EventGetRequest):
|
666
|
-
request_id = request.request_id
|
667
|
-
if not request_id:
|
668
|
-
raise HTTPException(
|
669
|
-
status_code=400, detail="request_id is required")
|
670
|
-
|
671
|
-
v = self.auto_coder_runner.get_event(request_id)
|
672
|
-
return v
|
673
|
-
|
674
|
-
@self.app.post("/api/event/response")
|
675
|
-
async def response_event(request: EventResponseRequest):
|
676
|
-
request_id = request.request_id
|
677
|
-
if not request_id:
|
678
|
-
raise HTTPException(
|
679
|
-
status_code=400, detail="request_id is required")
|
680
|
-
|
681
|
-
self.auto_coder_runner.response_event(
|
682
|
-
request_id, request.event, request.response)
|
683
|
-
return {"message": "success"}
|
684
|
-
|
685
440
|
@self.app.post("/api/commit")
|
686
441
|
async def commit():
|
687
442
|
try:
|
688
443
|
result = self.auto_coder_runner.commit()
|
689
444
|
return result
|
690
445
|
except Exception as e:
|
691
|
-
raise HTTPException(status_code=500, detail=str(e))
|
692
|
-
|
693
|
-
@self.app.get("/api/output/{request_id}")
|
694
|
-
async def get_terminal_logs(request_id: str):
|
695
|
-
return self.auto_coder_runner.get_logs(request_id)
|
446
|
+
raise HTTPException(status_code=500, detail=str(e))
|
696
447
|
|
697
448
|
@self.app.get("/api/last-yaml")
|
698
449
|
async def get_last_yaml():
|
699
450
|
"""Get information about the last YAML file"""
|
700
451
|
return JSONResponse(content=self.auto_coder_runner.get_last_yaml_info())
|
701
452
|
|
702
|
-
@self.app.post("/api/chat-lists/save")
|
703
|
-
async def save_chat_list(chat_list: ChatList):
|
704
|
-
try:
|
705
|
-
chat_lists_dir = os.path.join(
|
706
|
-
".auto-coder", "auto-coder.web", "chat-lists")
|
707
|
-
os.makedirs(chat_lists_dir, exist_ok=True)
|
708
|
-
|
709
|
-
file_path = os.path.join(
|
710
|
-
chat_lists_dir, f"{chat_list.name}.json")
|
711
|
-
async with aiofiles.open(file_path, 'w') as f:
|
712
|
-
await f.write(json.dumps({"messages": chat_list.messages}, indent=2))
|
713
|
-
return {"status": "success", "message": f"Chat list {chat_list.name} saved successfully"}
|
714
|
-
except Exception as e:
|
715
|
-
raise HTTPException(status_code=500, detail=str(e))
|
716
|
-
|
717
|
-
@self.app.get("/api/chat-lists")
|
718
|
-
async def get_chat_lists():
|
719
|
-
try:
|
720
|
-
chat_lists_dir = os.path.join(
|
721
|
-
".auto-coder", "auto-coder.web", "chat-lists")
|
722
|
-
os.makedirs(chat_lists_dir, exist_ok=True)
|
723
|
-
|
724
|
-
# Get files with their modification times
|
725
|
-
chat_lists = []
|
726
|
-
for file in os.listdir(chat_lists_dir):
|
727
|
-
if file.endswith('.json'):
|
728
|
-
file_path = os.path.join(chat_lists_dir, file)
|
729
|
-
mod_time = os.path.getmtime(file_path)
|
730
|
-
# Store tuple of (name, mod_time)
|
731
|
-
chat_lists.append((file[:-5], mod_time))
|
732
|
-
|
733
|
-
# Sort by modification time (newest first)
|
734
|
-
chat_lists.sort(key=lambda x: x[1], reverse=True)
|
735
|
-
|
736
|
-
# Return only the chat list names
|
737
|
-
return {"chat_lists": [name for name, _ in chat_lists]}
|
738
|
-
except Exception as e:
|
739
|
-
raise HTTPException(status_code=500, detail=str(e))
|
740
|
-
|
741
|
-
@self.app.get("/api/chat-lists/{name}")
|
742
|
-
async def get_chat_list(name: str):
|
743
|
-
try:
|
744
|
-
file_path = os.path.join(
|
745
|
-
".auto-coder", "auto-coder.web", "chat-lists", f"{name}.json")
|
746
|
-
if not os.path.exists(file_path):
|
747
|
-
raise HTTPException(
|
748
|
-
status_code=404, detail=f"Chat list {name} not found")
|
749
|
-
|
750
|
-
async with aiofiles.open(file_path, 'r') as f:
|
751
|
-
content = await f.read()
|
752
|
-
return json.loads(content)
|
753
|
-
except Exception as e:
|
754
|
-
raise HTTPException(status_code=500, detail=str(e))
|
755
|
-
|
756
|
-
@self.app.delete("/api/chat-lists/{name}")
|
757
|
-
async def delete_chat_list(name: str):
|
758
|
-
try:
|
759
|
-
file_path = os.path.join(
|
760
|
-
".auto-coder", "auto-coder.web", "chat-lists", f"{name}.json")
|
761
|
-
if not os.path.exists(file_path):
|
762
|
-
raise HTTPException(
|
763
|
-
status_code=404, detail=f"Chat list {name} not found")
|
764
|
-
|
765
|
-
os.remove(file_path)
|
766
|
-
return {"status": "success", "message": f"Chat list {name} deleted successfully"}
|
767
|
-
except Exception as e:
|
768
|
-
raise HTTPException(status_code=500, detail=str(e))
|
769
|
-
|
770
|
-
@self.app.post("/api/event/clear")
|
771
|
-
async def clear_events():
|
772
|
-
"""Clear all pending events in the event queue"""
|
773
|
-
try:
|
774
|
-
self.auto_coder_runner.clear_events()
|
775
|
-
return {"status": "success", "message": "Event queue cleared successfully"}
|
776
|
-
except Exception as e:
|
777
|
-
raise HTTPException(status_code=500, detail=str(e))
|
778
|
-
|
779
|
-
|
780
|
-
@self.app.get("/api/history/validate-and-load", response_model=ValidationResponseWithFileNumbers)
|
781
|
-
async def validate_and_load_queries():
|
782
|
-
try:
|
783
|
-
# 检查必要的目录
|
784
|
-
if not os.path.exists("actions") or not os.path.exists(".auto-coder"):
|
785
|
-
return ValidationResponseWithFileNumbers(
|
786
|
-
success=False,
|
787
|
-
message="无效的 auto-coder.chat 项目:缺少 actions 或 .auto-coder 目录"
|
788
|
-
)
|
789
|
-
|
790
|
-
queries = []
|
791
|
-
auto_coder_dir = "actions"
|
792
|
-
|
793
|
-
# 遍历actions目录下的所有yaml文件
|
794
|
-
for root, _, files in os.walk(auto_coder_dir):
|
795
|
-
for file in files:
|
796
|
-
if file.endswith('chat_action.yml'):
|
797
|
-
file_path = os.path.join(root, file)
|
798
|
-
match = re.match(r'(\d+)_chat_action\.yml', file)
|
799
|
-
if match:
|
800
|
-
file_number = int(match.group(1))
|
801
|
-
with open(file_path, 'r', encoding='utf-8') as f:
|
802
|
-
try:
|
803
|
-
yaml_content = yaml.safe_load(f)
|
804
|
-
if isinstance(yaml_content, dict) and 'query' in yaml_content:
|
805
|
-
timestamp = datetime.fromtimestamp(
|
806
|
-
os.path.getmtime(file_path)
|
807
|
-
).strftime('%Y-%m-%d %H:%M:%S')
|
808
|
-
|
809
|
-
file_md5 = hashlib.md5(open(file_path, 'rb').read()).hexdigest()
|
810
|
-
response_str = f"auto_coder_{file}_{file_md5}"
|
811
|
-
|
812
|
-
urls = yaml_content.get('urls', [])
|
813
|
-
|
814
|
-
queries.append(QueryWithFileNumber(
|
815
|
-
query=yaml_content['query'],
|
816
|
-
timestamp=timestamp,
|
817
|
-
file_number=file_number,
|
818
|
-
response=response_str,
|
819
|
-
urls=urls
|
820
|
-
))
|
821
|
-
except yaml.YAMLError:
|
822
|
-
continue
|
823
|
-
|
824
|
-
# 按时间戳排序
|
825
|
-
queries.sort(key=lambda x: x.timestamp or '', reverse=True)
|
826
|
-
|
827
|
-
return ValidationResponseWithFileNumbers(
|
828
|
-
success=True,
|
829
|
-
queries=queries
|
830
|
-
)
|
831
|
-
|
832
|
-
except Exception as e:
|
833
|
-
return ValidationResponseWithFileNumbers(
|
834
|
-
success=False,
|
835
|
-
message=f"读取项目文件时出错: {str(e)}"
|
836
|
-
)
|
837
|
-
|
838
|
-
@self.app.get("/api/history/commit-diff/{response_id}", response_model=CommitDiffResponse)
|
839
|
-
async def get_commit_diff(response_id: str):
|
840
|
-
"""根据response_id获取对应的git commit diff"""
|
841
|
-
try:
|
842
|
-
repo = git.Repo(self.project_path)
|
843
|
-
|
844
|
-
# 查找包含特定response message的commit
|
845
|
-
search_pattern = f"{response_id}"
|
846
|
-
|
847
|
-
matching_commits = []
|
848
|
-
for commit in repo.iter_commits():
|
849
|
-
if search_pattern in commit.message:
|
850
|
-
matching_commits.append(commit)
|
851
|
-
|
852
|
-
if not matching_commits:
|
853
|
-
return CommitDiffResponse(
|
854
|
-
success=False,
|
855
|
-
message=f"找不到对应的commit: {response_id}"
|
856
|
-
)
|
857
|
-
|
858
|
-
# 使用第一个匹配的commit
|
859
|
-
target_commit = matching_commits[0]
|
860
|
-
|
861
|
-
file_changes = []
|
862
|
-
if target_commit.parents:
|
863
|
-
parent = target_commit.parents[0]
|
864
|
-
diff = repo.git.diff(parent.hexsha, target_commit.hexsha)
|
865
|
-
|
866
|
-
# 获取变更的文件
|
867
|
-
diff_index = parent.diff(target_commit)
|
868
|
-
|
869
|
-
for diff_item in diff_index:
|
870
|
-
if diff_item.new_file:
|
871
|
-
file_changes.append(FileChange(
|
872
|
-
path=diff_item.b_path,
|
873
|
-
change_type="added"
|
874
|
-
))
|
875
|
-
else:
|
876
|
-
file_changes.append(FileChange(
|
877
|
-
path=diff_item.b_path,
|
878
|
-
change_type="modified"
|
879
|
-
))
|
880
|
-
else:
|
881
|
-
diff = repo.git.show(target_commit.hexsha)
|
882
|
-
|
883
|
-
# 对于初始commit,所有文件都是新增的
|
884
|
-
for item in target_commit.tree.traverse():
|
885
|
-
if item.type == 'blob': # 只处理文件,不处理目录
|
886
|
-
file_changes.append(FileChange(
|
887
|
-
path=item.path,
|
888
|
-
change_type="added"
|
889
|
-
))
|
890
|
-
|
891
|
-
return CommitDiffResponse(
|
892
|
-
success=True,
|
893
|
-
diff=diff,
|
894
|
-
file_changes=file_changes
|
895
|
-
)
|
896
|
-
|
897
|
-
except git.exc.GitCommandError as e:
|
898
|
-
return CommitDiffResponse(
|
899
|
-
success=False,
|
900
|
-
message=f"Git命令执行错误: {str(e)}"
|
901
|
-
)
|
902
|
-
except Exception as e:
|
903
|
-
return CommitDiffResponse(
|
904
|
-
success=False,
|
905
|
-
message=f"获取commit diff时出错: {str(e)}"
|
906
|
-
)
|
907
|
-
|
908
453
|
@self.app.get("/api/history/file-content/{file_number}", response_model=FileContentResponse)
|
909
454
|
async def get_file_content(file_number: int):
|
910
455
|
"""获取指定编号文件的完整内容"""
|