|
3 | 3 | from httpx import Client |
4 | 4 | from httpx_socks import SyncProxyTransport |
5 | 5 | from openai import NOT_GIVEN, NotGiven |
6 | | -from openai.types.chat import ChatCompletion, ChatCompletionReasoningEffort |
| 6 | +from openai.types.chat import ChatCompletion |
7 | 7 | from openai.types.chat.chat_completion_stream_options_param import ChatCompletionStreamOptionsParam |
8 | 8 | from openai.types.chat.chat_completion_message_param import ChatCompletionMessageParam |
9 | 9 | from openai.types.chat.chat_completion_tool_param import ChatCompletionToolParam |
|
12 | 12 | from ghostos.contracts.logger import LoggerItf, get_ghostos_logger |
13 | 13 | from ghostos.helpers import timestamp_ms |
14 | 14 | from ghostos.core.messages import ( |
15 | | - MessageStage, |
16 | 15 | Message, OpenAIMessageParser, DefaultOpenAIMessageParser, |
17 | 16 | CompletionUsagePayload, Role, |
18 | 17 | ) |
19 | 18 | from ghostos.core.llms import ( |
20 | | - LLMDriver, LLMApi, ModelConf, ServiceConf, OPENAI_DRIVER_NAME, LITELLM_DRIVER_NAME, DEEPSEEK_DRIVER_NAME, |
21 | | - Prompt, PromptPayload, PromptStorage, |
22 | | - Compatible, |
| 19 | + LLMApi, LLMDriver, |
| 20 | + ModelConf, ServiceConf, Compatible, |
| 21 | + OPENAI_DRIVER_NAME, LITELLM_DRIVER_NAME, DEEPSEEK_DRIVER_NAME, |
23 | 22 | FunctionalToken, |
| 23 | + Prompt, PromptPayload, PromptStorage |
24 | 24 | ) |
25 | 25 |
|
26 | | -__all__ = [ |
27 | | - 'OpenAIDriver', 'OpenAIAdapter', |
28 | | - 'LitellmAdapter', 'LiteLLMDriver', |
29 | | - 'DeepseekDriver', 'DeepseekAdapter', |
30 | | -] |
| 26 | +__all__ = ['OpenAIDriver', 'OpenAIAdapter'] |
31 | 27 |
|
32 | 28 |
|
33 | 29 | class FunctionalTokenPrompt(str): |
@@ -438,97 +434,3 @@ def driver_name(self) -> str: |
438 | 434 | def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: |
439 | 435 | get_ghostos_logger().debug(f"new llm api %s at service %s", model.model, service.name) |
440 | 436 | return OpenAIAdapter(service, model, self._parser, self._storage, self._logger, api_name=api_name) |
441 | | - |
442 | | - |
443 | | -class LitellmAdapter(OpenAIAdapter): |
444 | | - """ |
445 | | - adapter class wrap openai api to ghostos.blueprint.kernel.llms.LLMApi |
446 | | - """ |
447 | | - |
448 | | - def _chat_completion(self, chat: Prompt, stream: bool) -> ChatCompletion: |
449 | | - import litellm |
450 | | - messages = chat.get_messages() |
451 | | - messages = self.parse_message_params(messages) |
452 | | - response = litellm.completion( |
453 | | - model=self.model.model, |
454 | | - messages=list(messages), |
455 | | - timeout=self.model.timeout, |
456 | | - temperature=self.model.temperature, |
457 | | - n=self.model.n, |
458 | | - # not support stream yet |
459 | | - stream=False, |
460 | | - api_key=self.service.token, |
461 | | - ) |
462 | | - return response.choices[0].message |
463 | | - |
464 | | - def parse_message_params(self, messages: List[Message]) -> List[ChatCompletionMessageParam]: |
465 | | - parsed = super().parse_message_params(messages) |
466 | | - outputs = [] |
467 | | - count = 0 |
468 | | - for message in parsed: |
469 | | - # filter all the system message to __system__ user message. |
470 | | - if count > 0 and "role" in message and message["role"] == Role.SYSTEM.value: |
471 | | - message["role"] = Role.USER.value |
472 | | - message["name"] = "__system__" |
473 | | - outputs.append(message) |
474 | | - count += 1 |
475 | | - return outputs |
476 | | - |
477 | | - |
478 | | -# todo: move to lite_llm_driver. shall not locate here at very first. |
479 | | -class LiteLLMDriver(OpenAIDriver): |
480 | | - |
481 | | - def driver_name(self) -> str: |
482 | | - return LITELLM_DRIVER_NAME |
483 | | - |
484 | | - def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: |
485 | | - return LitellmAdapter(service, model, self._parser, self._storage, self._logger, api_name=api_name) |
486 | | - |
487 | | - |
488 | | -class DeepseekAdapter(OpenAIAdapter): |
489 | | - # def reasoning_completion(self, prompt: Prompt, stream: bool) -> Iterable[Message]: |
490 | | - # if not stream: |
491 | | - # yield from self._reasoning_completion_none_stream(prompt) |
492 | | - # else: |
493 | | - # yield from self._reasoning_completion_stream(prompt) |
494 | | - # |
495 | | - |
496 | | - def _from_openai_chat_completion_item(self, message: ChatCompletion) -> Iterable[Message]: |
497 | | - cc_item = message.choices[0].message |
498 | | - if reasoning := cc_item.reasoning_content: |
499 | | - reasoning_message = Message.new_tail(content=reasoning) |
500 | | - reasoning_message.stage = MessageStage.REASONING.value |
501 | | - yield reasoning_message |
502 | | - |
503 | | - yield self._parser.from_chat_completion(cc_item) |
504 | | - |
505 | | - def reasoning_completion_stream(self, prompt: Prompt) -> Iterable[ChatCompletionChunk]: |
506 | | - try: |
507 | | - chunks: Iterable[ChatCompletionChunk] = self._reasoning_completion_stream(prompt) |
508 | | - messages = self._from_openai_chat_completion_chunks(chunks) |
509 | | - prompt_payload = PromptPayload.from_prompt(prompt) |
510 | | - output = [] |
511 | | - for chunk in messages: |
512 | | - if not prompt.first_token: |
513 | | - prompt.first_token = timestamp_ms() |
514 | | - yield chunk |
515 | | - if chunk.is_complete(): |
516 | | - self.model.set_payload(chunk) |
517 | | - prompt_payload.set_payload(chunk) |
518 | | - output.append(chunk) |
519 | | - prompt.added = output |
520 | | - except Exception as e: |
521 | | - prompt.error = str(e) |
522 | | - raise |
523 | | - finally: |
524 | | - self._storage.save(prompt) |
525 | | - |
526 | | - |
527 | | -# todo: move to deepseek driver |
528 | | -class DeepseekDriver(OpenAIDriver): |
529 | | - |
530 | | - def driver_name(self) -> str: |
531 | | - return DEEPSEEK_DRIVER_NAME |
532 | | - |
533 | | - def new(self, service: ServiceConf, model: ModelConf, api_name: str = "") -> LLMApi: |
534 | | - return DeepseekAdapter(service, model, self._parser, self._storage, self._logger, api_name=api_name) |
0 commit comments