llama-github 0.1.0__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.
@@ -0,0 +1,348 @@
1
+ from llama_github.logger import logger
2
+ from llama_github.config.config import config
3
+ from typing import List, Optional, Any
4
+ from dataclasses import dataclass
5
+ from pprint import pformat
6
+
7
+ from llama_github.llm_integration.initial_load import LLMManager
8
+ from llama_github.llm_integration.llm_handler import LLMHandler
9
+ from llama_github.rag_processing.rag_processor import RAGProcessor
10
+
11
+ from llama_github.github_integration.github_auth_manager import GitHubAuthManager
12
+ from llama_github.data_retrieval.github_api import GitHubAPIHandler
13
+ from llama_github.data_retrieval.github_entities import Repository, RepositoryPool
14
+ from llama_github.utils import AsyncHTTPClient
15
+
16
+ import asyncio
17
+ from IPython import get_ipython
18
+ from concurrent.futures import ThreadPoolExecutor, as_completed
19
+ from urllib.parse import quote
20
+
21
+
22
+ @dataclass
23
+ class GitHubAppCredentials:
24
+ app_id: int
25
+ private_key: str
26
+ installation_id: int
27
+
28
+
29
+ class GithubRAG:
30
+ rag_processor: RAGProcessor = None
31
+
32
+ def __init__(self, github_access_token: Optional[str] = None,
33
+ github_app_credentials: Optional[GitHubAppCredentials] = None,
34
+ openai_api_key: Optional[str] = None,
35
+ huggingface_token: Optional[str] = None,
36
+ jina_api_key: Optional[str] = None,
37
+ open_source_models_hg_dir: Optional[str] = None,
38
+ embedding_model: Optional[str] = config.get(
39
+ "default_embedding"),
40
+ rerank_model: Optional[str] = config.get("default_reranker"),
41
+ llm: Any = None,
42
+ **kwargs) -> None:
43
+ """
44
+ Initialize the GithubRAG with the provided credentials and configuration.
45
+
46
+ Parameters:
47
+ - github_access_token (Optional[str]): GitHub access token for authentication.
48
+ - github_app_credentials (Optional[GitHubAppCredentials]): Credentials for GitHub App authentication.
49
+ - openai_api_key (Optional[str]): API key for OpenAI services -- recommend to use, GPT-4-turbo will be used.
50
+ - huggingface_token (Optional[str]): Token for Hugging Face services -- recommend to fill.
51
+ - jina_api_key (Optional[str]): API key for Jina AI services -- s.jina.ai API will be used
52
+ - open_source_models_hg_dir (Optional[str]): Name of open-source models from Hugging Face to replace OpenAI.
53
+ - embedding_model (Optional[str]): Name of Embedding model from Hugging Face, if you have preferred embedding model to be used.
54
+ - rerank_model (Optional[str]): Name of Rerank model from Hugging Face, if you have preferred rerank model to be used.
55
+ - llm (Any): Any kind of LangChain llm chat object - to replace OpenAI or open-source models from Hugging Face.
56
+ - **kwargs:
57
+ :param repo_cleanup_interval (Optional[int]): How often to run repo cleanup in seconds within RepositoryPool.
58
+ :param repo_max_idle_time (Optional[int]): Keep a repo in cache until max idle time if not used.
59
+
60
+ Returns:
61
+ - None
62
+ """
63
+ try:
64
+ logger.info("Initializing GithubRAG...")
65
+ logger.debug("Initializing Github Instance...")
66
+
67
+ self.auth_manager = GitHubAuthManager()
68
+ if github_access_token:
69
+ self.github_instance = self.auth_manager.authenticate_with_token(
70
+ github_access_token)
71
+ elif github_app_credentials:
72
+ self.github_instance = self.auth_manager.authenticate_with_app(
73
+ github_app_credentials.app_id, github_app_credentials.private_key, github_app_credentials.installation_id)
74
+ else:
75
+ logger.error("GitHub credentials not provided.")
76
+ logger.debug("Github Instance Initialized.")
77
+
78
+ logger.debug("Initializing Repository Pool...")
79
+ param_mapping = {
80
+ "repo_cleanup_interval": "cleanup_interval",
81
+ "repo_max_idle_time": "max_idle_time"
82
+ }
83
+ repo_pool_kwargs = {
84
+ param_mapping[k]: v for k, v in kwargs.items() if k in param_mapping}
85
+ self.RepositoryPool = RepositoryPool(
86
+ self.github_instance, **repo_pool_kwargs)
87
+ self.github_api_handler = GitHubAPIHandler(self.github_instance)
88
+ logger.debug("Repository Pool Initialized.")
89
+
90
+ self.jina_api_key = jina_api_key
91
+
92
+ logger.debug(
93
+ "Initializing llm manager, embedding model & reranker model...")
94
+ self.llm_manager = LLMManager(
95
+ openai_api_key, huggingface_token, open_source_models_hg_dir, embedding_model, rerank_model, llm)
96
+ logger.debug(
97
+ "LLM Manager, Embedding model & Reranker model Initialized.")
98
+
99
+ self.rag_processor = RAGProcessor(
100
+ self.github_api_handler, self.llm_manager)
101
+ except Exception as e:
102
+ logger.error(f"Error initializing GithubRAG: {e}")
103
+ raise e
104
+
105
+ async def async_retrieve_context(self, query, simple_mode=False) -> List[str]:
106
+ # Implementation of the context retrieval process
107
+ # This will involve using the GitHub API to search for relevant information,
108
+ # augmenting the retrieved data through the RAG methodology, and
109
+ # enhancing it with LLM capabilities.
110
+
111
+ topn_contexts = [] # This will be the list of context strings
112
+ try:
113
+ logger.info("Retrieving context...")
114
+ if simple_mode:
115
+ #In simple mode, only a Google search will be conducted based on the user's question.
116
+ #This model is not suitable for long questions (e.g., questions with more than 20 words).
117
+ task_google_search = asyncio.create_task(
118
+ self.google_search_retrieval(query=query))
119
+ await asyncio.gather(task_google_search)
120
+ logger.debug(
121
+ f"Google search: {str(len(task_google_search.result()))}")
122
+ context_list = self.rag_processor.arrange_context(
123
+ google_search_result=task_google_search.result())
124
+ if len(context_list)>0:
125
+ topn_contexts = await self.rag_processor.retrieve_topn_contexts(
126
+ context_list=context_list, query=query, top_n=config.get("top_n_contexts"))
127
+ else:
128
+ # Analyzing question and generating strategy
129
+ analyze_strategy = asyncio.create_task(
130
+ self.rag_processor.analyze_question(query))
131
+ # wait for generate analyze strategy
132
+ await analyze_strategy
133
+ analyzed_strategy = analyze_strategy.result()
134
+ logger.debug(f"Analyze strategy: {analyzed_strategy}")
135
+
136
+ # google search from GitHub
137
+ tokens = self.llm_manager.get_tokenizer().encode(query)
138
+ query_tokens = len(tokens)
139
+ task_google_search = asyncio.create_task(
140
+ self.google_search_retrieval(query=query if query_tokens < 20 else analyzed_strategy[0]))
141
+ # code search from GitHub
142
+ task_code_search = asyncio.create_task(
143
+ self.code_search_retrieval(query=analyzed_strategy[0], draft_answer=analyzed_strategy[1]+"\n\n"+analyzed_strategy[2]))
144
+ # issue search from GitHub
145
+ task_issue_search = asyncio.create_task(
146
+ self.issue_search_retrieval(query=analyzed_strategy[0], draft_answer=analyzed_strategy[1]+"\n\n"+analyzed_strategy[3]))
147
+ # repo search from GitHub
148
+ task_repo_search = asyncio.create_task(
149
+ self.repo_search_retrieval(query=analyzed_strategy[0]))
150
+
151
+ # wait for all tasks to complete
152
+ await asyncio.gather(task_google_search, task_code_search, task_issue_search, task_repo_search)
153
+
154
+ logger.debug(
155
+ f"Google search: {str(len(task_google_search.result()))}")
156
+ logger.debug(
157
+ f"Code search: {str(len(task_code_search.result()))}")
158
+ logger.debug(
159
+ f"Issue search: {str(len(task_issue_search.result()))}")
160
+ logger.debug(
161
+ f"Repo search: {str(len(task_repo_search.result()))}")
162
+
163
+ context_list = self.rag_processor.arrange_context(
164
+ code_search_result=task_code_search.result(),
165
+ issue_search_result=task_issue_search.result(),
166
+ repo_search_result=task_repo_search.result(),
167
+ google_search_result=task_google_search.result())
168
+
169
+ if len(context_list)>0:
170
+ topn_contexts = await self.rag_processor.retrieve_topn_contexts(
171
+ context_list=context_list, query=query, answer=analyzed_strategy[1], top_n=config.get("top_n_contexts"))
172
+
173
+ logger.info("Context retrieved successfully.")
174
+ except Exception as e:
175
+ logger.error(f"Error retrieving context: {e}")
176
+ raise e
177
+ return topn_contexts
178
+
179
+ def retrieve_context(self, query, simple_mode=False) -> List[str]:
180
+ """
181
+ Retrieve context from GitHub code, issue and repo search based on the input query.
182
+
183
+ Args:
184
+ query (str): The query or question to retrieve context for.
185
+
186
+ Returns:
187
+ List[str]: A list of context strings retrieved from the specified GitHub repositories.
188
+ """
189
+ self.loop = asyncio.get_event_loop()
190
+ ipython = get_ipython()
191
+ if ipython and ipython.has_trait('kernel'):
192
+ logger.debug("Running in Jupyter notebook, nest_asyncio applied.")
193
+ import nest_asyncio
194
+ nest_asyncio.apply()
195
+ return asyncio.run(self.async_retrieve_context(query, simple_mode=simple_mode))
196
+ if self.loop.is_running():
197
+ return asyncio.ensure_future(self.async_retrieve_context(query, simple_mode=simple_mode))
198
+ return self.loop.run_until_complete(self.async_retrieve_context(query, simple_mode=simple_mode))
199
+
200
+ async def code_search_retrieval(self, query, draft_answer: Optional[str] = None):
201
+ result = []
202
+ try:
203
+ logger.info("Retrieving code search...")
204
+ search_criterias = await self.rag_processor.get_code_search_criteria(query, draft_answer)
205
+ for search_criteria in search_criterias:
206
+ single_search_result = self.github_api_handler.search_code(
207
+ search_criteria.replace('"', ''))
208
+ for d in single_search_result:
209
+ result.append(d)
210
+ # deduplicate results
211
+ seen = set()
212
+ unique_list = []
213
+ for d in result:
214
+ value = d["url"]
215
+ if value not in seen and d["stargazers_count"] + config.get("code_search_max_hits") - d["index"] >= config.get("min_stars_to_keep_result"):
216
+ seen.add(value)
217
+ unique_list.append(d)
218
+ result = unique_list
219
+
220
+ logger.info("Code search retrieved successfully.")
221
+ except Exception as e:
222
+ logger.error(f"Error retrieving code search: {e}")
223
+
224
+ return result
225
+
226
+ async def issue_search_retrieval(self, query, draft_answer: Optional[str] = None):
227
+ result = []
228
+ try:
229
+ logger.info("Retrieving issue search...")
230
+ search_criterias = await self.rag_processor.get_issue_search_criteria(query, draft_answer)
231
+ for search_criteria in search_criterias:
232
+ single_search_result = self.github_api_handler.search_issues(
233
+ search_criteria.replace('"', ''))
234
+ for d in single_search_result:
235
+ result.append(d)
236
+ # deduplicate results
237
+ seen = set()
238
+ unique_list = []
239
+ for d in result:
240
+ value = d["url"]
241
+ if value not in seen:
242
+ seen.add(value)
243
+ unique_list.append(d)
244
+ result = unique_list
245
+
246
+ logger.info("Issue search retrieved successfully.")
247
+ except Exception as e:
248
+ logger.error(f"Error retrieving issue search: {e}")
249
+
250
+ return result
251
+
252
+ async def google_search_retrieval(self, query):
253
+ result = []
254
+ try:
255
+ logger.info("Retrieving google search...")
256
+ encoded_query = quote("site:github.com "+query)
257
+ url = f"https://s.jina.ai/{encoded_query}"
258
+ if self.jina_api_key is not None and self.jina_api_key != "":
259
+ headers = {
260
+ "Accept": "application/json",
261
+ "Authorization": f"Bearer {self.jina_api_key}"
262
+ }
263
+ retry_delay = 1
264
+ else:
265
+ headers = {
266
+ "Accept": "application/json"
267
+ }
268
+ retry_delay = 20
269
+
270
+ response = await AsyncHTTPClient.request(url, headers=headers, retry_count=2, retry_delay=retry_delay)
271
+ urls = []
272
+ urls = [item["url"] for item in response["data"] if "url" in item]
273
+
274
+ for github_url in urls:
275
+ content = self.github_api_handler.get_github_url_content(
276
+ github_url)
277
+ if content and content != "":
278
+ result.append({
279
+ 'url': github_url,
280
+ 'content': content
281
+ })
282
+ logger.info(f"Google search retrieved successfully:{urls}")
283
+ except Exception as e:
284
+ logger.error(f"Error retrieving google search: {e}")
285
+ return result
286
+
287
+ def _get_repository_rag_info(self, repository: Repository):
288
+ """
289
+ Helper method to get file content through a Repository object.
290
+
291
+ :param code_search_result: A single code search result.
292
+ :return: Tuple containing the Repository object and the file content.
293
+ """
294
+ # Assuming RepositoryPool is accessible and initialized somewhere in this class
295
+ return repository.get_readme(), self.rag_processor.get_repo_simple_structure(repository)
296
+
297
+ async def repo_search_retrieval(self, query, draft_answer: Optional[str] = None):
298
+ result = []
299
+ results_with_index = []
300
+ try:
301
+ logger.info("Retrieving repo search...")
302
+ search_criterias = await self.rag_processor.get_repo_search_criteria(query, draft_answer)
303
+ for search_criteria in search_criterias:
304
+ single_search_result = self.github_api_handler.search_repositories(
305
+ search_criteria.replace('"', ''))
306
+ for d in single_search_result:
307
+ result.append(d)
308
+ # deduplicate results
309
+ seen = set()
310
+ unique_list = []
311
+ for d in result:
312
+ value = d.full_name
313
+ if value not in seen:
314
+ seen.add(value)
315
+ unique_list.append(d)
316
+ repositories = unique_list
317
+
318
+ with ThreadPoolExecutor(max_workers=config.get("max_workers")) as executor:
319
+ # Concurrently fetch the file content for each code search result
320
+ future_to_index = {executor.submit(
321
+ self._get_repository_rag_info, repository): index for index, repository in enumerate(repositories)}
322
+ for future in as_completed(future_to_index):
323
+ index = future_to_index[future]
324
+ repository = repositories[index]
325
+ try:
326
+ repo_readme, repo_simple_structure = future.result()
327
+ if repo_readme is None or repository.description is None or repository.description == "" or repo_simple_structure == "{}":
328
+ continue
329
+ if repo_readme:
330
+ results_with_index.append({
331
+ 'index': index,
332
+ 'full_name': repository.full_name,
333
+ 'content': repo_readme,
334
+ })
335
+ # if repo_simple_structure:
336
+ # results_with_index.append({
337
+ # 'index': index,
338
+ # 'full_name': repository.full_name,
339
+ # 'content': "The repository "+repository.full_name+" with description:" + repository.description+" has below repo simple structure:\n"+repo_simple_structure,
340
+ # })
341
+ except Exception as e:
342
+ logger.error(
343
+ f"Error getting repository info: {e}")
344
+
345
+ logger.info("Repo search retrieved successfully.")
346
+ except Exception as e:
347
+ logger.error(f"Error retrieving repos search: {e}")
348
+ return results_with_index
File without changes
@@ -0,0 +1,101 @@
1
+ # initial_load.py
2
+ import torch
3
+ from typing import Optional, Any
4
+ from threading import Lock
5
+ from langchain_openai import ChatOpenAI
6
+ from langchain_community.embeddings import HuggingFaceEmbeddings
7
+ from langchain_community.cross_encoders import HuggingFaceCrossEncoder
8
+
9
+ from llama_github.config.config import config
10
+ from llama_github.logger import logger
11
+
12
+ from transformers import AutoModel
13
+ from transformers import AutoModelForSequenceClassification
14
+ from transformers import AutoTokenizer
15
+
16
+
17
+ class LLMManager:
18
+ _instance_lock = Lock()
19
+ _instance = None
20
+ llm = None
21
+ # embedding_model = None
22
+ rerank_model = None
23
+ _initialized = False
24
+ llm_simple = None
25
+ tokenizer = None
26
+
27
+ def __new__(cls, *args, **kwargs):
28
+ if cls._instance is None: # First check (unlocked)
29
+ with cls._instance_lock: # Acquire lock
30
+ if cls._instance is None: # Second check (locked)
31
+ cls._instance = super(LLMManager, cls).__new__(cls)
32
+ return cls._instance
33
+
34
+ def __init__(self,
35
+ openai_api_key: Optional[str] = None,
36
+ huggingface_token: Optional[str] = None,
37
+ open_source_models_hg_dir: Optional[str] = None,
38
+ embedding_model: Optional[str] = config.get(
39
+ "default_embedding"),
40
+ rerank_model: Optional[str] = config.get("default_reranker"),
41
+ llm: Any = None):
42
+ with self._instance_lock: # Prevent re-initialization
43
+ if self._initialized:
44
+ return
45
+ self._initialized = True
46
+
47
+ # Initialize for OpenAI GPT-4
48
+ if llm is not None:
49
+ self.llm = llm
50
+ self.model_type = "Custom_langchain_llm"
51
+ elif openai_api_key is not None and openai_api_key != "" and self.llm is None:
52
+ logger.info("Initializing OpenAI API...")
53
+ self.llm = ChatOpenAI(api_key=openai_api_key, model="gpt-4-turbo")
54
+ self.llm_simple = ChatOpenAI(
55
+ api_key=openai_api_key, model="gpt-3.5-turbo")
56
+ self.model_type = "OpenAI"
57
+ # Initialize for Open Source Models
58
+ elif open_source_models_hg_dir is not None and open_source_models_hg_dir != "" and self.llm is None:
59
+ logger.info(f"Initializing {open_source_models_hg_dir}...")
60
+ # load hugingface models
61
+ self.model_type = "Hubgingface"
62
+ elif self.llm is None:
63
+ # default model is phi3_mini_128k
64
+ self.model_type = "Hubgingface"
65
+
66
+ # initial model_kwargs
67
+ if torch.cuda.is_available():
68
+ self.device = torch.device('cuda')
69
+ elif torch.backends.mps.is_available():
70
+ self.device = torch.device('mps')
71
+ else:
72
+ self.device = torch.device('cpu')
73
+
74
+ # initial embedding_model
75
+ if self.tokenizer is None:
76
+ logger.info(f"Initializing {embedding_model}...")
77
+ self.tokenizer = AutoTokenizer.from_pretrained(embedding_model)
78
+ self.embedding_model = AutoModel.from_pretrained(
79
+ embedding_model, trust_remote_code=True).to(self.device)
80
+
81
+ # initial rerank_model
82
+ if self.rerank_model is None:
83
+ logger.info(f"Initializing {rerank_model}...")
84
+ self.rerank_model = AutoModelForSequenceClassification.from_pretrained(
85
+ rerank_model, num_labels=1, trust_remote_code=True
86
+ ).to(self.device)
87
+
88
+ def get_llm(self):
89
+ return self.llm
90
+
91
+ def get_llm_simple(self):
92
+ return self.llm_simple
93
+
94
+ def get_tokenizer(self):
95
+ return self.tokenizer
96
+
97
+ def get_rerank_model(self):
98
+ return self.rerank_model
99
+
100
+ def get_embedding_model(self):
101
+ return self.embedding_model
@@ -0,0 +1,131 @@
1
+ # llm_handler.py
2
+ # to do list
3
+ # 1. add streaming output for invoke.
4
+
5
+ from llama_github.llm_integration.initial_load import LLMManager
6
+ from langchain_core.prompts import ChatPromptTemplate, ChatMessagePromptTemplate, MessagesPlaceholder
7
+ from langchain_core.output_parsers import StrOutputParser
8
+ from llama_github.config.config import config
9
+ from llama_github.logger import logger
10
+ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
11
+ from langchain_core.pydantic_v1 import BaseModel
12
+ from typing import Optional
13
+ from langchain_openai import output_parsers
14
+
15
+
16
+ class LLMHandler:
17
+ def __init__(self, llm_manager: Optional[LLMManager] = None):
18
+ """
19
+ Initializes the LLMHandler class which is responsible for handling the interaction
20
+ with a language model (LLM) using the LangChain framework.
21
+
22
+ Attributes:
23
+ llm_manager (LLMManager): Manages interactions with the language model.
24
+ """
25
+ if llm_manager is not None:
26
+ self.llm_manager = llm_manager
27
+ else:
28
+ self.llm_manager = LLMManager()
29
+
30
+ async def ainvoke(self, human_question: str, chat_history: Optional[list[str]] = None, context: Optional[list[str]] = None, output_structure: Optional[BaseModel] = None, prompt: str = config.get("general_prompt"), simple_llm=False) -> str:
31
+ """
32
+ Asynchronously invokes the language model with a given question, chat history, and context,
33
+ and returns the model's response.
34
+
35
+ Parameters:
36
+ human_question (str): The question or input from the human user.
37
+ chat_history (list[str]): A list of strings representing the chat history, where each
38
+ string is a message. This parameter is optional.
39
+ context (list[str]): A list of strings representing additional context for the model.
40
+ This parameter is optional.
41
+ output_structure: A langchain_core.pydantic_v1.BaseModel object to control desired
42
+ structure of the output from the language model.
43
+ This parameter is optional and allows for more detailed control over
44
+ the model's responses.
45
+ prompt (str): A template for the prompt to be used with the language model. Defaults
46
+ to a general prompt defined in the configuration.
47
+
48
+ Returns:
49
+ str: The response from the language model.
50
+ """
51
+ try:
52
+ if simple_llm and self.llm_manager.get_llm_simple() is not None:
53
+ llm = self.llm_manager.get_llm_simple()
54
+ else:
55
+ llm = self.llm_manager.get_llm()
56
+ if self.llm_manager.model_type == "OpenAI":
57
+ # Create a prompt template with placeholders for dynamic content.
58
+ prompt_template = ChatMessagePromptTemplate.from_template(
59
+ role="system", template=prompt)
60
+ chat_prompt = ChatPromptTemplate.from_messages([
61
+ prompt_template,
62
+ MessagesPlaceholder(
63
+ variable_name="history_messages", optional=True),
64
+ MessagesPlaceholder(variable_name="human_message"),
65
+ MessagesPlaceholder(
66
+ variable_name="context_messages", optional=True)
67
+ ])
68
+
69
+ # Convert chat_history and context from [str] to their respective message types.
70
+ chat_history_messages = self._compose_context_messages(
71
+ chat_history)
72
+ context_messages = self._compose_chat_history_messages(context)
73
+ human_question_message = HumanMessage(content=human_question)
74
+
75
+ prompt_params = {
76
+ "history_messages": chat_history_messages,
77
+ "human_message": [human_question_message],
78
+ "context_messages": context_messages
79
+ }
80
+
81
+ # Format the prompt with the provided parameters.
82
+ formatted_prompt = chat_prompt.format_prompt(**prompt_params)
83
+ logger.debug(f"Formatted prompt: {formatted_prompt}")
84
+ # Determine the processing chain based on the presence of an output structure.
85
+ if output_structure is not None:
86
+ chain = llm.with_structured_output(output_structure)
87
+ else:
88
+ chain = llm
89
+
90
+ # Invoke the chain and return the model's response.
91
+ try:
92
+ response = await chain.ainvoke(formatted_prompt.to_messages())
93
+ except Exception as e:
94
+ logger.exception(
95
+ f"Call llm with #{human_question}# generated an exception:{e}")
96
+ if output_structure is not None:
97
+ response = await chain.ainvoke(formatted_prompt.to_messages())
98
+ return response
99
+ except Exception as e:
100
+ logger.exception(
101
+ f"Call llm with #{human_question}# generated an exception:{e}")
102
+ return "An error occurred during processing."
103
+
104
+ def _compose_chat_history_messages(self, chat_history: list[str]) -> list:
105
+ """
106
+ Converts chat history from a list of strings to a list of alternating HumanMessage
107
+ and AIMessage objects, starting with HumanMessage.
108
+
109
+ Parameters:
110
+ chat_history (list[str]): The chat history as a list of strings.
111
+
112
+ Returns:
113
+ list: A list of alternating HumanMessage and AIMessage objects.
114
+ """
115
+ messages = []
116
+ for i, message in enumerate(chat_history or []):
117
+ message_class = HumanMessage if i % 2 == 0 else AIMessage
118
+ messages.append(message_class(content=message))
119
+ return messages
120
+
121
+ def _compose_context_messages(self, context: list[str]) -> list:
122
+ """
123
+ Converts context from a list of strings to a list of SystemMessage objects.
124
+
125
+ Parameters:
126
+ context (list[str]): The context as a list of strings.
127
+
128
+ Returns:
129
+ list: A list of SystemMessage objects.
130
+ """
131
+ return [SystemMessage(content=message) for message in context or []]
llama_github/logger.py ADDED
@@ -0,0 +1,28 @@
1
+ import logging
2
+
3
+ logger = logging.getLogger('llama_github')
4
+
5
+ def configure_logging(level=logging.INFO, handler=None):
6
+ logger.setLevel(level)
7
+ if handler:
8
+ logger.addHandler(handler)
9
+ else:
10
+ # default handler output to console
11
+ ch = logging.StreamHandler()
12
+ ch.setLevel(level)
13
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
14
+ ch.setFormatter(formatter)
15
+ logger.addHandler(ch)
16
+
17
+ ## Logging Configuration
18
+ # By default, `llama_github` does not configure logging to avoid interfering with your application's logging settings. If you want to see log outputs from `llama_github`, you can configure logging as follows:
19
+
20
+ # ```python
21
+ # import logging
22
+ # import llama_github
23
+
24
+ # # Configure the library's logger
25
+ # llama_github.configure_logging(level=logging.INFO)
26
+
27
+ # # Now you can see log outputs
28
+ # logger = logging.getLogger('llama_github')
File without changes