add http sevice

This commit is contained in:
jinli.yl 2025-06-10 10:51:37 +08:00
parent 79a247e114
commit a17dc024fa
10 changed files with 226 additions and 132 deletions

View file

@ -8,6 +8,7 @@ from pydantic import Field, BaseModel
from experiencemaker.model.base_llm import BaseLLM
from experiencemaker.module.prompt.prompt_mixin import PromptMixin
from experiencemaker.schema.trajectory import Message, ActionMessage, ToolCall, StateMessage
from experiencemaker.tool import CodeTool, DashscopeSearchTool, TerminateTool
from experiencemaker.tool.base_tool import BaseTool
@ -23,7 +24,7 @@ class SimpleAgentContext(BaseModel):
class SimpleAgent(PromptMixin):
llm: BaseLLM | None = Field(default=None)
max_steps: int = Field(default=10)
tools: List[BaseTool] = Field(default_factory=list)
tools: List[BaseTool] = [CodeTool(), DashscopeSearchTool(), TerminateTool()]
prompt_file_path: Path = Path(__file__).parent / "simple_agent_prompt.yaml"
def think(self, context: SimpleAgentContext):

View file

@ -14,7 +14,6 @@ from experiencemaker.utils.registry import Registry
class BaseContextGenerator(BaseModel, ABC):
vector_store: BaseVectorStore | None = Field(default=None)
llm: BaseLLM | None = Field(default=None)
embedding_model: BaseEmbeddingModel | None = Field(default=None)
workspace_id: str = Field(default="")
def _build_retrieve_query(self, trajectory: Trajectory, **kwargs) -> str:

View file

@ -2,7 +2,7 @@ from abc import ABC
from pydantic import BaseModel, Field
from experiencemaker.module.agent_wrapper.base_agent_wrapper import BaseAgentWrapper
from experiencemaker.module.agent_wrapper.agent_wrapper_mixin import AgentWrapperMixin
from experiencemaker.module.context_generator.base_context_generator import BaseContextGenerator
from experiencemaker.module.environment.base_environment import BaseEnvironment
from experiencemaker.module.summarizer.base_summarizer import BaseSummarizer
@ -10,7 +10,7 @@ from experiencemaker.module.summarizer.base_summarizer import BaseSummarizer
class BaseEvaluator(BaseModel, ABC):
data_path: str = Field(default="")
agent_wrapper: BaseAgentWrapper | None = Field(default=None)
agent_wrapper: AgentWrapperMixin | None = Field(default=None)
context_generator: BaseContextGenerator | None = Field(default=None)
summarizer: BaseSummarizer | None = Field(default=None)
env: BaseEnvironment | None = Field(default=None)

View file

@ -2,7 +2,7 @@ from typing import List
from pydantic import BaseModel, Field
from experiencemaker.module.agent_wrapper.base_agent_wrapper import BaseAgentWrapper
from experiencemaker.module.agent_wrapper.agent_wrapper_mixin import AgentWrapperMixin
from experiencemaker.module.context_generator.base_context_generator import BaseContextGenerator
from experiencemaker.module.environment.base_environment import BaseEnvironment
from experiencemaker.module.summarizer.base_summarizer import BaseSummarizer
@ -10,7 +10,7 @@ from experiencemaker.schema.trajectory import Trajectory
class BaseRunner(BaseModel):
agent_wrapper: BaseAgentWrapper | None = Field(default=None)
agent_wrapper: AgentWrapperMixin | None = Field(default=None)
context_generator: BaseContextGenerator | None = Field(default=None)
summarizer: BaseSummarizer | None = Field(default=None)
env: BaseEnvironment | None = Field(default=None)

View file

@ -3,7 +3,6 @@ from typing import List
from pydantic import Field, BaseModel
from experiencemaker.model.base_embedding_model import BaseEmbeddingModel
from experiencemaker.model.base_llm import BaseLLM
from experiencemaker.schema.experience import Experience
from experiencemaker.schema.trajectory import Trajectory
@ -15,7 +14,6 @@ from experiencemaker.utils.registry import Registry
class BaseSummarizer(BaseModel, ABC):
vector_store: BaseVectorStore | None = Field(default=None)
llm: BaseLLM | None = Field(default=None)
embedding_model: BaseEmbeddingModel | None = Field(default=None)
workspace_id: str = Field(default="")
def _extract_experiences(self, trajectories: List[Trajectory], **kwargs) -> List[Experience]:

View file

@ -1,13 +1,12 @@
from abc import ABC
from typing import List
from pydantic import Field
from pydantic import BaseModel, Field
from experiencemaker.schema.module_loader import ModuleLoader
from experiencemaker.schema.trajectory import Trajectory
class BaseRequest(ModuleLoader, ABC):
class BaseRequest(BaseModel, ABC):
metadata: dict = Field(default_factory=dict)
@ -21,4 +20,4 @@ class ContextGeneratorRequest(BaseRequest):
class SummarizerRequest(BaseRequest):
trajectories: List[Trajectory] = Field(default_factory=dict)
return_samples: bool = Field(default=False)
return_experience: bool = Field(default=True)

View file

@ -3,7 +3,8 @@ from typing import List
from pydantic import BaseModel, Field
from experiencemaker.schema.trajectory import Trajectory, ContextMessage, Sample
from experiencemaker.schema.experience import Experience
from experiencemaker.schema.trajectory import Trajectory, ContextMessage
class BaseResponse(BaseModel, ABC):
@ -20,4 +21,4 @@ class ContextGeneratorResponse(BaseResponse):
class SummarizerResponse(BaseResponse):
extract_samples: List[Sample] = Field(default_factory=list)
experiences: List[Experience] = Field(default_factory=list)

View file

@ -1,118 +0,0 @@
from typing import List
import uvicorn
from fastapi import FastAPI
from loguru import logger
from experiencemaker.model.base_embedding_model import BaseEmbeddingModel, EMBEDDING_MODEL_REGISTRY
from experiencemaker.model.base_llm import BaseLLM, LLM_REGISTRY
from experiencemaker.module.agent_wrapper.base_agent_wrapper import BaseAgentWrapper
from experiencemaker.module.context_generator.base_context_generator import BaseContextGenerator
from experiencemaker.module.summarizer.base_summarizer import BaseSummarizer
from experiencemaker.schema.request import AgentWrapperRequest, ContextGeneratorRequest, SummarizerRequest
from experiencemaker.schema.response import AgentWrapperResponse, ContextGeneratorResponse, SummarizerResponse
from experiencemaker.schema.trajectory import ContextMessage, Trajectory, Sample
from experiencemaker.storage.base_vector_store import BaseVectorStore, VECTOR_STORE_REGISTRY
app = FastAPI()
from pydantic import BaseModel, Field, model_validator
class ExperienceMakerHttpService(BaseModel):
host: str = Field(default="0.0.0.0")
port: int = Field(default=8001)
timeout_keep_alive: int = Field(default=600000)
limit_concurrency: int = Field(default=32)
llm_config: dict = Field(default_factory=dict)
embedding_model_config: dict = Field(default_factory=dict)
vector_store_config: dict = Field(default_factory=dict)
agent_wrapper_config: dict = Field(default_factory=dict)
context_generator_config: dict = Field(default_factory=dict)
summarizer_config: dict = Field(default_factory=dict)
llm: BaseLLM | None = Field(default=None)
embedding_model: BaseEmbeddingModel | None = Field(default=None)
vector_store: BaseVectorStore | None = Field(default=None)
agent_wrapper: BaseAgentWrapper | None = Field(default=None)
context_generator: BaseContextGenerator | None = Field(default=None)
summarizer: BaseSummarizer | None = Field(default=None)
@staticmethod
def init_llm(llm_config: dict):
backend = llm_config.pop("backend", None)
assert backend is not None, "llm must have a backend like `openai_compatible`."
assert backend in LLM_REGISTRY, f"llm backend={backend} not supported. supported backend={LLM_REGISTRY.registered_modules}"
llm = LLM_REGISTRY[backend](**llm_config)
logger.info(f"llm is inited with backend={backend} params={llm_config}")
return llm
@staticmethod
def init_embedding_model(embedding_model_config: dict):
backend = embedding_model_config.pop("backend", None)
assert backend is not None, "embedding_model must have a backend like `openai_compatible`."
assert backend in EMBEDDING_MODEL_REGISTRY, f"embedding_model backend={backend} not supported. supported backend={EMBEDDING_MODEL_REGISTRY.registered_modules}"
embedding_model = EMBEDDING_MODEL_REGISTRY[backend](**embedding_model_config)
logger.info(f"embedding_model is inited with backend={backend} params={embedding_model_config}")
return embedding_model
@staticmethod
def init_vector_store(vector_store_config: dict):
backend = vector_store_config.pop("backend", None)
assert backend is not None, "vector_store must have a backend like `elasticsearch`."
assert backend in VECTOR_STORE_REGISTRY, f"vector_store backend={backend} not supported. supported backend={VECTOR_STORE_REGISTRY.registered_modules}"
vector_store = VECTOR_STORE_REGISTRY[backend](**vector_store_config)
logger.info(f"vector_store is inited with backend={backend} params={vector_store_config}")
return vector_store
@model_validator(mode="after")
def init_modules(self):
if self.llm_config:
self.llm = self.init_llm(self.llm_config)
if self.embedding_model_config:
self.embedding_model = self.init_embedding_model(self.embedding_model_config)
if self.vector_store_config:
self.vector_store = self.init_vector_store(self.vector_store_config)
@app.post('/agent_wrapper', response_model=AgentWrapperResponse)
def call_agent_wrapper(request: AgentWrapperRequest):
module: BaseAgentWrapper = request.load_from_path()
trajectory: Trajectory = module.execute(request.query, **request.metadata)
return AgentWrapperResponse(trajectory=trajectory)
@app.post('/context_generator', response_model=ContextGeneratorResponse)
def call_context_generator(request: ContextGeneratorRequest):
module: BaseContextGenerator = request.load_from_path()
context_msg: ContextMessage = module.execute(request.trajectory, **request.metadata)
return ContextGeneratorResponse(context_msg=context_msg)
@app.post('/summarizer', response_model=SummarizerResponse)
def call_summarizer(request: SummarizerRequest):
module: BaseSummarizer = request.load_from_path()
samples: List[Sample] = module.execute(request.trajectories, request.return_samples, **request.metadata)
return SummarizerResponse(extract_samples=samples)
if __name__ == '__main__':
uvicorn.run(app, host="0.0.0.0", port=8000, timeout_keep_alive=600000, limit_concurrency=32)
# from experiencemaker.config import summarizer_config
# print(summarizer_config.simple)
from experiencemaker.config.config_handler import ConfigHandler
summarizer_config = ConfigHandler(module_name="summarizer")
context_generator_config = ConfigHandler(module_name="context_generator")
print(context_generator_config.config_dict)
# launch with:
# python -m experiencemaker.service.model_service

View file

@ -0,0 +1,164 @@
from typing import List
from loguru import logger
from pydantic import BaseModel, Field, model_validator
from experiencemaker.model.base_embedding_model import BaseEmbeddingModel, EMBEDDING_MODEL_REGISTRY
from experiencemaker.model.base_llm import BaseLLM, LLM_REGISTRY
from experiencemaker.module.agent_wrapper.agent_wrapper_mixin import AGENT_WRAPPER_REGISTRY, AgentWrapperMixin
from experiencemaker.module.context_generator.base_context_generator import BaseContextGenerator, \
CONTEXT_GENERATOR_REGISTRY
from experiencemaker.module.summarizer.base_summarizer import BaseSummarizer, SUMMARIZER_REGISTRY
from experiencemaker.schema.experience import Experience
from experiencemaker.schema.request import AgentWrapperRequest, ContextGeneratorRequest, SummarizerRequest
from experiencemaker.schema.response import AgentWrapperResponse, ContextGeneratorResponse, SummarizerResponse
from experiencemaker.schema.trajectory import Trajectory, ContextMessage
from experiencemaker.storage.base_vector_store import BaseVectorStore, VECTOR_STORE_REGISTRY
class ExperienceMakerService(BaseModel):
host: str = Field(default="0.0.0.0")
port: int = Field(default=8001)
timeout_keep_alive: int = Field(default=600000)
limit_concurrency: int = Field(default=32)
llm_config: dict = Field(default_factory=dict)
embedding_model_config: dict = Field(default_factory=dict)
vector_store_config: dict = Field(default_factory=dict)
agent_wrapper_config: dict = Field(default_factory=dict)
context_generator_config: dict = Field(default_factory=dict)
summarizer_config: dict = Field(default_factory=dict)
llm: BaseLLM | None = Field(default=None)
embedding_model: BaseEmbeddingModel | None = Field(default=None)
vector_store: BaseVectorStore | None = Field(default=None)
agent_wrapper: AgentWrapperMixin | None = Field(default=None)
context_generator: BaseContextGenerator | None = Field(default=None)
summarizer: BaseSummarizer | None = Field(default=None)
@staticmethod
def init_llm(llm_config: dict) -> BaseLLM:
backend = llm_config.pop("backend", None)
assert backend is not None, "llm must have a backend like `openai_compatible`."
assert backend in LLM_REGISTRY, f"llm backend={backend} not supported. " \
f"supported={LLM_REGISTRY.registered_modules}"
llm = LLM_REGISTRY[backend](**llm_config)
logger.info(f"llm is inited with backend={backend} params={llm_config}")
return llm
def get_llm(self, config: dict, llm: BaseLLM = None) -> BaseLLM:
if "llm" in config:
llm_config = config.pop("llm")
llm = self.init_llm(llm_config)
elif llm is None:
raise RuntimeError("llm must be provided.")
return llm
@staticmethod
def init_embedding_model(embedding_model_config: dict) -> BaseEmbeddingModel:
backend = embedding_model_config.pop("backend", None)
assert backend is not None, "embedding_model must have a backend like `openai_compatible`."
assert backend in EMBEDDING_MODEL_REGISTRY, f"embedding_model backend={backend} not supported. " \
f"supported={EMBEDDING_MODEL_REGISTRY.registered_modules}"
embedding_model = EMBEDDING_MODEL_REGISTRY[backend](**embedding_model_config)
logger.info(f"embedding_model is inited with backend={backend} params={embedding_model_config}")
return embedding_model
def get_embedding_model(self, config: dict, embedding_model: BaseEmbeddingModel = None) -> BaseEmbeddingModel:
if "embedding_model" in config:
embedding_model_config = config.pop("embedding_model")
embedding_model = self.init_embedding_model(embedding_model_config)
elif embedding_model is None:
raise RuntimeError("embedding_model must be provided.")
return embedding_model
def init_vector_store(self, vector_store_config: dict) -> BaseVectorStore:
backend = vector_store_config.pop("backend", None)
assert backend is not None, "vector_store must have a backend like `elasticsearch`."
assert backend in VECTOR_STORE_REGISTRY, f"vector_store backend={backend} not supported. " \
f"supported={VECTOR_STORE_REGISTRY.registered_modules}"
embedding_model = self.get_embedding_model(vector_store_config, embedding_model=self.embedding_model)
vector_store = VECTOR_STORE_REGISTRY[backend](**vector_store_config, embedding_model=embedding_model)
logger.info(f"vector_store is inited with backend={backend} params={vector_store_config}")
return vector_store
def get_vector_store(self, config: dict, vector_store: BaseVectorStore = None) -> BaseVectorStore:
if "vector_store" in config:
vector_store_config = config.pop("vector_store")
vector_store = self.init_vector_store(vector_store_config)
elif vector_store is None:
raise RuntimeError("vector_store must be provided.")
return vector_store
def init_context_generator(self, context_generator_config: dict) -> BaseContextGenerator:
backend = context_generator_config.pop("backend", None)
assert backend is not None, "context_generator must have a backend like `simple`."
assert backend in CONTEXT_GENERATOR_REGISTRY, f"context_generator backend={backend} not supported. " \
f"supported={CONTEXT_GENERATOR_REGISTRY.registered_modules}"
llm = self.get_llm(context_generator_config, llm=self.llm)
vector_store = self.get_vector_store(context_generator_config, vector_store=self.vector_store)
context_generator: BaseContextGenerator = CONTEXT_GENERATOR_REGISTRY[backend](
**context_generator_config, llm=llm, vector_store=vector_store)
logger.info(f"context_generator is inited with backend={backend} params={context_generator_config}")
return context_generator
def init_summarizer(self, summarizer_config: dict) -> BaseSummarizer:
backend = summarizer_config.pop("backend", None)
assert backend is not None, "summarizer must have a backend like `simple`."
assert backend in SUMMARIZER_REGISTRY, f"summarizer backend={backend} not supported. " \
f"supported={SUMMARIZER_REGISTRY.registered_modules}"
llm = self.get_llm(summarizer_config, llm=self.llm)
vector_store = self.get_vector_store(summarizer_config, vector_store=self.vector_store)
summarizer: BaseSummarizer = SUMMARIZER_REGISTRY[backend](**summarizer_config,
llm=llm, vector_store=vector_store)
logger.info(f"summarizer is inited with backend={backend} params={summarizer_config}")
return summarizer
def init_agent_wrapper(self, agent_wrapper_config: dict) -> AgentWrapperMixin:
backend = agent_wrapper_config.pop("backend", None)
assert backend is not None, "agent_wrapper must have a backend like `simple`."
assert backend in AGENT_WRAPPER_REGISTRY, f"agent_wrapper backend={backend} not supported. " \
f"supported={AGENT_WRAPPER_REGISTRY.registered_modules}"
llm = self.get_llm(agent_wrapper_config, llm=self.llm)
agent_wrapper: AgentWrapperMixin = AGENT_WRAPPER_REGISTRY[backend](
**agent_wrapper_config, llm=llm, context_generator=self.context_generator)
logger.info(f"agent_wrapper is inited with backend={backend} params={agent_wrapper_config}")
return agent_wrapper
@model_validator(mode="after")
def init_modules(self):
if self.llm_config:
self.llm = self.init_llm(self.llm_config)
if self.embedding_model_config:
self.embedding_model = self.init_embedding_model(self.embedding_model_config)
if self.vector_store_config:
self.vector_store = self.init_vector_store(self.vector_store_config)
if self.context_generator_config:
self.context_generator = self.init_context_generator(self.context_generator_config)
if self.summarizer_config:
self.summarizer = self.init_summarizer(self.summarizer_config)
if self.agent_wrapper_config:
self.agent_wrapper = self.init_agent_wrapper(self.agent_wrapper_config)
def call_agent_wrapper(self, request: AgentWrapperRequest) -> AgentWrapperResponse:
assert self.agent_wrapper is not None, "agent_wrapper must be provided."
trajectory: Trajectory = self.agent_wrapper.execute(request.query, **request.metadata)
return AgentWrapperResponse(trajectory=trajectory)
def call_context_generator(self, request: ContextGeneratorRequest) -> ContextGeneratorResponse:
assert self.context_generator is not None, "context_generator must be provided."
context_msg: ContextMessage = self.context_generator.execute(request.trajectory, **request.metadata)
return ContextGeneratorResponse(context_msg=context_msg)
def call_summarizer(self, request: SummarizerRequest) -> SummarizerResponse:
assert self.summarizer is not None, "summarizer must be provided."
experiences: List[Experience] = self.summarizer.execute(request.trajectories, request.return_experience,
**request.metadata)
return SummarizerResponse(experiences=experiences)

View file

@ -0,0 +1,50 @@
import argparse
import json
import uvicorn
from fastapi import FastAPI
from experiencemaker.schema.request import AgentWrapperRequest, ContextGeneratorRequest, SummarizerRequest
from experiencemaker.schema.response import AgentWrapperResponse, ContextGeneratorResponse, SummarizerResponse
from experiencemaker.service.experience_maker_service import ExperienceMakerService
from experiencemaker.utils.file_handler import FileHandler
app = FastAPI()
service: ExperienceMakerService | None = None
@app.post('/agent_wrapper', response_model=AgentWrapperResponse)
def call_agent_wrapper(request: AgentWrapperRequest):
return service.call_agent_wrapper(request)
@app.post('/context_generator', response_model=ContextGeneratorResponse)
def call_context_generator(request: ContextGeneratorRequest):
return service.call_context_generator(request)
@app.post('/summarizer', response_model=SummarizerResponse)
def call_summarizer(request: SummarizerRequest):
return service.call_summarizer(request)
# launch with: python -m experiencemaker.service.http_service
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--config', type=str, help='config dict')
parser.add_argument('--config_path', type=str, help='config load path')
args = parser.parse_args()
if args.config_path:
config = FileHandler(file_path=args.config_path).load()
elif args.config:
config = json.loads(args.config)
else:
raise RuntimeError("both config and config_path are not specified")
service = ExperienceMakerService(**config)
uvicorn.run(app,
host=service.host,
port=service.port,
timeout_keep_alive=service.timeout_keep_alive,
limit_concurrency=service.limit_concurrency)