Skip to content

Commit

Permalink
specrepair code adapt for fast api
Browse files Browse the repository at this point in the history
  • Loading branch information
tzing_t committed Sep 14, 2024
1 parent 616410d commit 126ace6
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 91 deletions.
6 changes: 6 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
RELOAD=True

BASE_URL=http://localhost:8000

# SpecBot
SPECBOT_AI_MODEL=gpt-4-0613
REPAIR_PRO_AI_MODEL=claude-3-5-sonnet-20240620
OPENAI_API_KEY=*** Provide your API Key ***
OPENAI_BASE_URL=*** Provide your Base url ***
33 changes: 33 additions & 0 deletions infra_ai_service/api/ai_enhance/spec_repair_process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from fastapi import APIRouter, File, UploadFile

from infra_ai_service.service.spec_repair import SpecBot


router = APIRouter()


@router.post("/")
async def spec_repair_process(err_spec_file: UploadFile = File(...),
err_log_file: UploadFile = File(...)):
try:
err_spec_lines = await err_spec_file.read()
err_log_lines = await err_log_file.read()

err_spec_lines = err_spec_lines.decode().splitlines(keepends=True)
err_log_lines = err_log_lines.decode().splitlines(keepends=True)

bot = SpecBot()
suggestion, is_repaired, repaired_spec_lines, log_content = bot.repair(
err_spec_lines, err_log_lines)

return {
'suggestions': suggestion,
'repair_status': is_repaired,
'repair_spec': repaired_spec_lines,
'log': log_content
}
except Exception as e:
return {
'status': 'error',
'message': str(e)
}
2 changes: 1 addition & 1 deletion infra_ai_service/api/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def setup_qdrant_environment():
try:
qdrant_client.get_collection(collection_name)
print(f"Collection {collection_name} already exists")
except HTTPException as e:
except Exception as e:
# 获取向量维度
sample_embedding = next(fastembed_model.embed(["Sample text"]))
vector_size = len(sample_embedding)
Expand Down
4 changes: 4 additions & 0 deletions infra_ai_service/api/router.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from fastapi.routing import APIRouter

from infra_ai_service.api.ai_enhance.spec_repair_process import \
router as spec_repair_process
from infra_ai_service.api.ai_enhance.text_process import \
router as text_process_router
from infra_ai_service.api.ai_enhance.embedding import \
Expand All @@ -8,6 +10,8 @@
router as vector_search_router

api_router = APIRouter()
api_router.include_router(spec_repair_process, prefix="/spec-repair",
tags=["repair"])
api_router.include_router(text_process_router, prefix="/text", tags=["text"])
api_router.include_router(embedding_router, prefix="/embedding",
tags=["embedding"])
Expand Down
30 changes: 26 additions & 4 deletions infra_ai_service/config/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ class Settings(BaseSettings):
"""Application settings."""

ENV: str = "dev"
HOST: str = "0.0.0.0"
HOST: str = 'localhost'
PORT: int = 8000
_BASE_URL: str = f"https://{HOST}:{PORT}"
# quantity of workers for uvicorn
WORKERS_COUNT: int = 1
# Enable uvicorn reloading
RELOAD: bool = False

# SpecBot config
SPECBOT_AI_MODEL: str = ''
REPAIR_PRO_AI_MODEL: str = ''
OPENAI_API_KEY: str = ''
OPENAI_BASE_URL: str = ''

@property
def BASE_URL(self) -> str:
return self._BASE_URL if self._BASE_URL.endswith(
"/") else f"{self._BASE_URL}/"
return f"https://{self.HOST}:{self.PORT}/"

class Config:
env_file = f"{BASE_DIR}/.env"
Expand All @@ -33,6 +37,24 @@ class Config:
"_DB_BASE": {
"env": "DB_BASE",
},
'HOST': {
'env': 'HOST',
},
'PORT': {
'env': 'PORT',
},
'SPECBOT_AI_MODEL': {
'env': 'SPECBOT_AI_MODEL'
},
'REPAIR_PRO_AI_MODEL': {
'env': 'REPAIR_PRO_AI_MODEL'
},
'OPENAI_API_KEY': {
'env': 'OPENAI_API_KEY'
},
'OPENAI_BASE_URL': {
'env': 'OPENAI_BASE_URL'
}
}


Expand Down
2 changes: 1 addition & 1 deletion infra_ai_service/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ def get_app() -> FastAPI:
default_response_class=UJSONResponse,
)

app.include_router(router=api_router, prefix="/api")
app.include_router(router=api_router, prefix="/api/v1")

return app
2 changes: 1 addition & 1 deletion infra_ai_service/service/spec_repair/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .bot import SpecBot
from infra_ai_service.service.spec_repair.bot import SpecBot

__all__ = ["SpecBot"]
143 changes: 99 additions & 44 deletions infra_ai_service/service/spec_repair/bot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#!/usr/bin/python3

import os
import re
import json
from copy import deepcopy
from openai import OpenAI
from .utils import (
from infra_ai_service.config.config import settings
from infra_ai_service.service.spec_repair.utils import (
gen_func_description,
repair_spec,
repair_spec_pro,
Expand All @@ -11,7 +15,9 @@
save_log,
)

SYSTEM_PROMPT = "你是一位经验丰富RPM软件包构建人员,你的任务是根据提供的spec脚本和报错日志修复spec脚本,以解决构建过程中出现的问题。"
SYSTEM_PROMPT = ("你是一位经验丰富RPM软件包构建人员,"
"你的任务是根据提供的spec脚本和报错日志修复spec脚本,"
"以解决构建过程中出现的问题。")

PROMPT_TEMPLATE = """
spec脚本:
Expand Down Expand Up @@ -68,14 +74,29 @@

class SpecBot:
def __init__(self):
api_key = os.getenv("OPENAI_API_KEY", None)
base_url = os.getenv("OPENAI_BASE_URL", None)
api_key = settings.OPENAI_API_KEY
base_url = settings.OPENAI_BASE_URL
self.client = OpenAI(api_key=api_key, base_url=base_url)
self.model = "gpt-4-0613"
self.model = settings.SPECBOT_AI_MODEL

def repair(self, spec_lines: list, log_lines: list):
'''
repair spec file content
:param spec_lines: content of error spec file
:type list
:param log_lines: content of error log
:type list
:return
:type tuple(str, bool, list(str), str)
'''

spec = self._preprocess_spec(deepcopy(spec_lines))
log = self._preprocess_log(log_lines)

def repair(self, input_spec, input_log, output_spec, output_log):
spec = self._preprocess_spec(input_spec)
log = self._preprocess_log(input_log)
tools = self._prepare_tools()
messages = self._prepare_messages(spec, log)
fault_segment = None
Expand All @@ -87,7 +108,10 @@ def repair(self, input_spec, input_log, output_spec, output_log):
model=self.model,
messages=messages,
tools=tools,
tool_choice={"type": "function", "function": {"name": "repair_spec"}},
tool_choice={
"type": "function",
"function": {"name": "repair_spec"}
},
)
tool_calls = response.choices[0].message.tool_calls
arguments = tool_calls[0].function.arguments
Expand All @@ -97,34 +121,56 @@ def repair(self, input_spec, input_log, output_spec, output_log):
repaired_segment = arguments.get("repaired_segment", None)

if suggestion and fault_segment and repaired_segment:
is_repaired = repair_spec_impl(
input_spec, fault_segment, repaired_segment, output_spec
is_repaired, repaired_spec_lines = repair_spec_impl(
deepcopy(spec_lines), fault_segment, repaired_segment
)
except Exception as e:
suggestion = str(e)

patch = get_patch(input_spec, output_spec) if is_repaired else None
save_log(output_log, is_repaired, log, suggestion, fault_segment, patch)
if is_repaired:
patch = get_patch(spec_lines, repaired_spec_lines)
else:
patch = None
log_content = save_log(is_repaired,
log,
suggestion,
fault_segment,
patch)

return suggestion, is_repaired
repaired_spec_str = ''.join(repaired_spec_lines)
return suggestion, is_repaired, repaired_spec_str, log_content

def repair_pro(self, input_spec, input_log, input_doc, output_spec, output_log):
spec = self._preprocess_spec(input_spec)
log = self._preprocess_log(input_log)
doc = self._prepare_doc(input_doc)
def repair_pro(self, spec_lines, log_lines, doc_content=None):
'''
repair spec file content with doc
:param spec_lines: content of error spec file
:type list
:param log_lines: content of error log
:type list
:return
:type tuple(str, bool, list(str), str)
'''
spec = self._preprocess_spec(deepcopy(spec_lines))
log = self._preprocess_log(log_lines)
tools = self._prepare_tools_pro()
fault_segment = None
repaired_segment = None

is_repaired = False
try:
messages = self._prepare_messages_pro_1(spec, log, doc)
messages = self._prepare_messages_pro_1(spec, log, doc_content)
response = self.client.chat.completions.create(
model="claude-3-5-sonnet-20240620", messages=messages
model=settings.REPAIR_PRO_AI_MODEL, messages=messages
)
suggestion = response.choices[0].message.content

messages = self._prepare_messages_pro_2(spec, suggestion, doc)
messages = self._prepare_messages_pro_2(spec,
suggestion,
doc_content)
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
Expand All @@ -141,25 +187,37 @@ def repair_pro(self, input_spec, input_log, input_doc, output_spec, output_log):
repaired_segment = arguments.get("repaired_segment", None)

if suggestion and fault_segment and repaired_segment:
is_repaired = repair_spec_impl(
input_spec, fault_segment, repaired_segment, output_spec
is_repaired, repaired_spec_lines = repair_spec_impl(
deepcopy(spec_lines), fault_segment, repaired_segment
)

except Exception as e:
suggestion = str(e)

patch = get_patch(input_spec, output_spec) if is_repaired else None
save_log(output_log, is_repaired, log, suggestion, fault_segment, patch)
if is_repaired:
patch = get_patch(spec_lines, repaired_spec_lines)
else:
patch = None

return suggestion, is_repaired
log_content = save_log(is_repaired,
log,
suggestion,
fault_segment,
patch)

repaired_spec_str = ''.join(repaired_spec_lines)
return suggestion, is_repaired, repaired_spec_str, log_content

def _prepare_messages(self, spec, log):
# 准备消息
messages = []
if SYSTEM_PROMPT:
messages.append({"role": "system", "content": SYSTEM_PROMPT})
messages.append(
{"role": "user", "content": PROMPT_TEMPLATE.format(spec=spec, log=log)}
{
"role": "user",
"content": PROMPT_TEMPLATE.format(spec=spec, log=log)
}
)
return messages

Expand All @@ -170,7 +228,9 @@ def _prepare_messages_pro_1(self, spec, log, doc):
messages.append(
{
"role": "user",
"content": PROMPT_TEMPLATE_PRO_1.format(spec=spec, log=log, doc=doc),
"content": PROMPT_TEMPLATE_PRO_1.format(spec=spec,
log=log,
doc=doc),
}
)
return messages
Expand All @@ -180,7 +240,9 @@ def _prepare_messages_pro_2(self, spec, info, doc):
messages.append(
{
"role": "user",
"content": PROMPT_TEMPLATE_PRO_2.format(spec=spec, info=info, doc=doc),
"content": PROMPT_TEMPLATE_PRO_2.format(spec=spec,
info=info,
doc=doc),
}
)
return messages
Expand All @@ -193,17 +255,10 @@ def _prepare_tools_pro(self):
# 准备工具
return [gen_func_description(repair_spec_pro)]

def _prepare_doc(self, doc_file):
if doc_file is None:
return None
with open(doc_file, "r", encoding="utf-8") as f:
doc = f.read()
return doc

def _preprocess_spec(self, spec_file):
# 预处理spec
with open(spec_file, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
def _preprocess_spec(self, lines: list):
'''
pre-process spec file content
'''
index = 0
for i in range(len(lines)):
lines[i] = f"{index}: " + lines[i]
Expand All @@ -219,10 +274,10 @@ def _preprocess_spec(self, spec_file):
spec = "".join(lines[start_index:])
return spec

def _preprocess_log(self, log_file):
# 预处理log
with open(log_file, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
def _preprocess_log(self, lines: list):
'''
pre-process log file content
'''
start_index = 0
end_index = len(lines)

Expand Down
Loading

0 comments on commit 126ace6

Please sign in to comment.