nucore-ai 1.0.0__tar.gz

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.
Files changed (36) hide show
  1. nucore_ai-1.0.0/LICENSE +21 -0
  2. nucore_ai-1.0.0/MANIFEST.in +1 -0
  3. nucore_ai-1.0.0/PKG-INFO +84 -0
  4. nucore_ai-1.0.0/README.md +69 -0
  5. nucore_ai-1.0.0/pyproject.toml +25 -0
  6. nucore_ai-1.0.0/requirements.txt +4 -0
  7. nucore_ai-1.0.0/setup.cfg +4 -0
  8. nucore_ai-1.0.0/src/assistant/assistant.py +372 -0
  9. nucore_ai-1.0.0/src/config/__init__.py +3 -0
  10. nucore_ai-1.0.0/src/config/config.py +68 -0
  11. nucore_ai-1.0.0/src/nucore/__init__.py +17 -0
  12. nucore_ai-1.0.0/src/nucore/cmd.py +61 -0
  13. nucore_ai-1.0.0/src/nucore/editor.py +183 -0
  14. nucore_ai-1.0.0/src/nucore/linkdef.py +39 -0
  15. nucore_ai-1.0.0/src/nucore/loader.py +323 -0
  16. nucore_ai-1.0.0/src/nucore/node.py +58 -0
  17. nucore_ai-1.0.0/src/nucore/nodedef.py +87 -0
  18. nucore_ai-1.0.0/src/nucore/nucore.py +532 -0
  19. nucore_ai-1.0.0/src/nucore/nucore_backend_api.py +329 -0
  20. nucore_ai-1.0.0/src/nucore/nucore_programs.py +73 -0
  21. nucore_ai-1.0.0/src/nucore/profile.py +40 -0
  22. nucore_ai-1.0.0/src/nucore/uom.py +935 -0
  23. nucore_ai-1.0.0/src/nucore_ai.egg-info/PKG-INFO +84 -0
  24. nucore_ai-1.0.0/src/nucore_ai.egg-info/SOURCES.txt +34 -0
  25. nucore_ai-1.0.0/src/nucore_ai.egg-info/dependency_links.txt +1 -0
  26. nucore_ai-1.0.0/src/nucore_ai.egg-info/requires.txt +4 -0
  27. nucore_ai-1.0.0/src/nucore_ai.egg-info/top_level.txt +5 -0
  28. nucore_ai-1.0.0/src/rag/__init__.py +9 -0
  29. nucore_ai-1.0.0/src/rag/device_rag_formatter.py +189 -0
  30. nucore_ai-1.0.0/src/rag/rag_data_struct.py +46 -0
  31. nucore_ai-1.0.0/src/rag/rag_formatter.py +28 -0
  32. nucore_ai-1.0.0/src/rag/rag_processor.py +302 -0
  33. nucore_ai-1.0.0/src/rag/reranker.py +43 -0
  34. nucore_ai-1.0.0/src/rag/static_info_rag_formatter.py +58 -0
  35. nucore_ai-1.0.0/src/rag/tools_rag_formatter.py +52 -0
  36. nucore_ai-1.0.0/src/server/start_servers.py +126 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 NuCoreAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ include requirements.txt
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.4
2
+ Name: nucore-ai
3
+ Version: 1.0.0
4
+ Summary: Lightweight library for command/control/optimization/automation of smart home devices using LLMs
5
+ Author-email: Michel Kohanim <michel@universal-devices.com>, "P.A. SCHEMBRI" <pa.schembri@advanced-stack.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/NuCoreAI/nucore-ai.git
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: requests
11
+ Requires-Dist: transformers
12
+ Requires-Dist: sentence-transformers
13
+ Requires-Dist: chromadb
14
+ Dynamic: license-file
15
+
16
+ # NuCoreAI Platform!
17
+
18
+ ## Goal
19
+
20
+ This library goal is to convert a user query written in natural language to commands, queries, and programs in any NuCore enabled platform (currently eisy).
21
+
22
+ ## Quick start
23
+
24
+ Installation:
25
+
26
+ ```shell
27
+ git clone https://github.com/NuCoreAI/nucore-ai.git
28
+ ```
29
+
30
+ ## llama.cpp compile and build
31
+ 1. Download llama.cpp
32
+ 2. Go to the directory and do as per one of the options below:
33
+
34
+ ### No GPU
35
+ ```shell
36
+ cmake -B build.blis -DGGML_BLAS=on -DGGML_BLAS_VENDOR=FLAME
37
+ ```
38
+ followed by
39
+ ```shell
40
+ cmake --build build.blis --conifg release
41
+ ```
42
+ This will install llama.cpp binaries in build.blis directory local to llama.cpp installation. The reason we are using build.blis directory is that you may want to experiment with the GPU version
43
+
44
+ ### Nvidia GPU
45
+ On Ubuntu:
46
+ ```shell
47
+ sudo ubuntu-drivers install
48
+ sudo apt install nvidia-utils-{latest version}
49
+ sudo apt install nvidia-cuda-toolkit
50
+ ```
51
+ Now you are ready to build:
52
+ ```shell
53
+ cmake -B build.cuda -DGGML_CUDA=on
54
+ ```
55
+ followed by
56
+ ```shell
57
+ cmake --build build.cuda --config release
58
+ ```
59
+ If you have x running, you may want to have it release resources. First use nvidia-smi utility to see what's running and how much memory is being used by other things:
60
+ ```shell
61
+ sudo nvidia-smi
62
+ ```
63
+ if anything is running and using memory:
64
+ 1. Make the prime display point to the integrated one (say intel)
65
+ ```shell
66
+ sudo prime-select intel
67
+ ```
68
+ 2. Then, make it on demand
69
+ ```shell
70
+ sudo prime-select on-demand
71
+ ```
72
+ 3. Make sure your system sees it:
73
+ ```shell
74
+ nvidia-smi
75
+ ```
76
+
77
+ ## The Model
78
+ You are going to be using a finetuned version of Qwen2.5-Coder-7B [here](https://huggingface.co/mkohanim/qwen2.5-coder-nucore-7b)
79
+
80
+ ## Testing
81
+ For testing, you can either use a live eisy or use example profiles/nodes [here](https://github.com/NuCoreAI/ai-workflow)
82
+
83
+ ## Documentation
84
+ The code is very well documented but we have not yet made and official documentation.
@@ -0,0 +1,69 @@
1
+ # NuCoreAI Platform!
2
+
3
+ ## Goal
4
+
5
+ This library goal is to convert a user query written in natural language to commands, queries, and programs in any NuCore enabled platform (currently eisy).
6
+
7
+ ## Quick start
8
+
9
+ Installation:
10
+
11
+ ```shell
12
+ git clone https://github.com/NuCoreAI/nucore-ai.git
13
+ ```
14
+
15
+ ## llama.cpp compile and build
16
+ 1. Download llama.cpp
17
+ 2. Go to the directory and do as per one of the options below:
18
+
19
+ ### No GPU
20
+ ```shell
21
+ cmake -B build.blis -DGGML_BLAS=on -DGGML_BLAS_VENDOR=FLAME
22
+ ```
23
+ followed by
24
+ ```shell
25
+ cmake --build build.blis --conifg release
26
+ ```
27
+ This will install llama.cpp binaries in build.blis directory local to llama.cpp installation. The reason we are using build.blis directory is that you may want to experiment with the GPU version
28
+
29
+ ### Nvidia GPU
30
+ On Ubuntu:
31
+ ```shell
32
+ sudo ubuntu-drivers install
33
+ sudo apt install nvidia-utils-{latest version}
34
+ sudo apt install nvidia-cuda-toolkit
35
+ ```
36
+ Now you are ready to build:
37
+ ```shell
38
+ cmake -B build.cuda -DGGML_CUDA=on
39
+ ```
40
+ followed by
41
+ ```shell
42
+ cmake --build build.cuda --config release
43
+ ```
44
+ If you have x running, you may want to have it release resources. First use nvidia-smi utility to see what's running and how much memory is being used by other things:
45
+ ```shell
46
+ sudo nvidia-smi
47
+ ```
48
+ if anything is running and using memory:
49
+ 1. Make the prime display point to the integrated one (say intel)
50
+ ```shell
51
+ sudo prime-select intel
52
+ ```
53
+ 2. Then, make it on demand
54
+ ```shell
55
+ sudo prime-select on-demand
56
+ ```
57
+ 3. Make sure your system sees it:
58
+ ```shell
59
+ nvidia-smi
60
+ ```
61
+
62
+ ## The Model
63
+ You are going to be using a finetuned version of Qwen2.5-Coder-7B [here](https://huggingface.co/mkohanim/qwen2.5-coder-nucore-7b)
64
+
65
+ ## Testing
66
+ For testing, you can either use a live eisy or use example profiles/nodes [here](https://github.com/NuCoreAI/ai-workflow)
67
+
68
+ ## Documentation
69
+ The code is very well documented but we have not yet made and official documentation.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "nucore-ai"
7
+ version = "1.0.0"
8
+ description = "Lightweight library for command/control/optimization/automation of smart home devices using LLMs"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ { name="Michel Kohanim", email="michel@universal-devices.com" },
13
+ { name="P.A. SCHEMBRI", email="pa.schembri@advanced-stack.com" }
14
+ ]
15
+ dependencies = [
16
+ "requests",
17
+ "transformers",
18
+ "sentence-transformers",
19
+ "chromadb",
20
+ ]
21
+ urls = { "Homepage" = "https://github.com/NuCoreAI/nucore-ai.git" }
22
+
23
+ [tool.setuptools]
24
+ package-dir = {"" = "src"}
25
+ packages = ["assistant", "config", "rag", "nucore", "server"]
@@ -0,0 +1,4 @@
1
+ requests
2
+ transformers
3
+ sentence-transformers
4
+ chromadb
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,372 @@
1
+ import re
2
+ import requests, os
3
+ import json
4
+ import httpx
5
+ import asyncio, argparse
6
+
7
+ from nucore import NuCore
8
+ from config import AIConfig
9
+
10
+ """
11
+ Best option
12
+ build.cuda/bin/llama-server -m /home/michel/workspace/nucore/models/finetuned/qwen2.5-coder-dls-7b/qwen2.5-coder-dls-7b-Q4_K_M.gguf --jinja --host localhost -c 60000 --port 8013 -t 15 --n-gpu-layers 32 --batch-size 8192
13
+ """
14
+ from importlib.resources import files
15
+
16
+ # Assuming this code is inside your_package/module.py
17
+ data_path = files('assistant').joinpath('nucore.system.prompt')
18
+
19
+ with data_path.open('r', encoding='utf-8') as f:
20
+ system_prompt = f.read().strip()
21
+
22
+ config = AIConfig()
23
+
24
+ class NuCoreAssistant:
25
+ def __init__(self, args, websocket=None):
26
+ self.websocket=websocket
27
+ self.sent_system_prompt=False
28
+ if not args:
29
+ raise ValueError("Arguments are required to initialize NuCoreAssistant")
30
+ self.nuCore = NuCore(
31
+ collection_path=args.collection_path if args.collection_path else os.path.join(os.path.expanduser("~"), ".nucore_db"),
32
+ collection_name="nucore.assistant",
33
+ backend_url=args.url,
34
+ backend_username=args.username,
35
+ backend_password=args.password
36
+ )
37
+ model_url = args.model_url+"/v1/chat/completion" if args.model_url else config.getModelURL()
38
+ if not model_url:
39
+ raise ValueError("Model URL is required to initialize NuCoreAssistant")
40
+ self.__model_url__ = model_url
41
+ self.__model_auth_token__ = args.model_auth_token if args.model_auth_token else None
42
+ print (self.__model_url__)
43
+ self.nuCore.load()
44
+
45
+ def set_remote_model_access_token(self, token: str):
46
+ """
47
+ You are responsible for refreshing the access token
48
+ Set the remote model access token.
49
+ :param token: The access token to set.
50
+ """
51
+ self.__model_auth_token__ = token
52
+
53
+ async def create_automation_routine(self,routines:list):
54
+ """
55
+ Create automation routines in NuCore.
56
+ :param routines: A list of routines to create.
57
+ :return: The result of the routine creation.
58
+ **for now, just a stub **
59
+ """
60
+ return self.nuCore.create_automation_routines(routines)
61
+
62
+ async def process_property_query(self, prop_query:list):
63
+ if not prop_query or len(prop_query) == 0:
64
+ return "No property query provided"
65
+ for property in prop_query:
66
+ # Process the property query
67
+ device_id = property.get('device_id')
68
+ if not device_id:
69
+ print(f"No device ID provided for property query: {property}")
70
+ continue
71
+ properties = await self.nuCore.get_properties(device_id)
72
+ if not properties:
73
+ print(f"No properties found for device {property['device_id']}")
74
+ continue
75
+ prop_id = property.get('property_id')
76
+ prop_name = property.get('property_name')
77
+ device_name = self.nuCore.get_device_name(device_id)
78
+ if not device_name:
79
+ device_name = device_id
80
+ if prop_id:
81
+ prop = properties.get(prop_id)
82
+ if prop:
83
+ text = f"\nNuCore: {prop_name if prop_name else prop_id} for {device_name} is: {prop.formatted if prop.formatted else prop.value}"
84
+ #await self.send_user_content_to_llm(text)
85
+ await self.send_response(text, True)
86
+ else:
87
+ print( f"Property {prop_id} not found for device {property['device_id']}")
88
+ else:
89
+ print(f"No property ID provided for device {property['device_id']}")
90
+
91
+
92
+ async def process_tool_call(self,full_response:str, begin_marker, end_marker):
93
+ if not full_response or not begin_marker or not end_marker:
94
+ return None
95
+
96
+ #we need an ordered command list to process. The order is important:
97
+ # first command must run first and second next etc.
98
+ parameters = [] #ordered set of commands
99
+
100
+ command_pattern = re.compile(rf'{begin_marker}(.*?){end_marker}', re.DOTALL)
101
+ matches = command_pattern.findall(full_response)
102
+
103
+ for match in matches:
104
+ try:
105
+ # Remove any comments that start with //, # or /* and end with */
106
+ match = re.sub(r'//.*?$|#.*?$|/\*.*?\*/', '', match, flags=re.MULTILINE)
107
+ # Remove any leading or trailing whitespace
108
+ match = match.strip()
109
+ if not match:
110
+ continue
111
+ # Parse the JSON block
112
+ parameter_json = json.loads(match.strip())
113
+ parameters.append(parameter_json)
114
+ except json.JSONDecodeError as e:
115
+ print(f"Error decoding JSON from nucore block: {e}")
116
+ return None
117
+
118
+ if len(parameters) > 0:
119
+ if begin_marker == "__BEGIN_NUCORE_COMMAND__":
120
+ return await self.nuCore.send_commands(parameters)
121
+
122
+ elif begin_marker == "__BEGIN_NUCORE_PROPERTY_QUERY__":
123
+ return await self.process_property_query(parameters)
124
+
125
+ return None
126
+
127
+ async def send_response(self, message, is_end=False):
128
+ if not message:
129
+ return
130
+ if self.websocket:
131
+ await self.websocket.send_json({
132
+ "sender": "bot",
133
+ "message": message,
134
+ "end": 'true' if is_end else 'false'
135
+ })
136
+ else:
137
+ print(message, end="", flush=True)
138
+
139
+ async def send_user_content_to_llm(self, user_content):
140
+ """
141
+ Send user content to the LLM for processing.
142
+ :param user_content: The content provided by the user.
143
+ """
144
+ if not user_content:
145
+ print("No user content provided, exiting ...")
146
+ return
147
+ user_message = {
148
+ "role": "user",
149
+ "content": f"{user_content.strip()}"
150
+ }
151
+ messages = [user_message]
152
+ payload={
153
+ "messages": messages,
154
+ "stream": False,
155
+ "temperature": 2.0,
156
+ "max_tokens": 60_000,
157
+ }
158
+
159
+ response = requests.post(self.__model_url__, json=payload, headers={
160
+ "Authorization": f"Bearer {self.__model_auth_token__}" if self.__model_auth_token__ else "",
161
+ })
162
+ response.raise_for_status()
163
+ await self.send_response(response.json()["choices"][0]["message"]["content"])
164
+ return None
165
+
166
+ async def process_customer_input(self, query:str, num_rag_results=5, rerank=True):
167
+ """
168
+ Process the customer input by sending it to the AI model and handling the response.
169
+ :param query: The customer input to process.
170
+ :param num_rag_results: The number of RAG results to use for the actual query
171
+ :param rerank: Whether to rerank the results.
172
+ """
173
+
174
+ if not query:
175
+ print("No query provided, exiting ...")
176
+ messages =[]
177
+
178
+ device_docs = ""
179
+ if not self.nuCore.load_devices(include_profiles=False):
180
+ raise ValueError("Failed to load devices from NuCore. Please check your configuration.")
181
+
182
+ # Load RAG documents
183
+ #if not self.nuCore.load_rag_docs(dump=False):
184
+ # raise ValueError("Failed to load RAG documents from NuCore. Please check your configuration.")
185
+ rag = self.nuCore.format_nodes()
186
+ if not rag:
187
+ raise ValueError(f"Warning: No RAG documents found for node {self.nuCore.url}. Skipping.")
188
+
189
+ rag_docs = rag["documents"]
190
+ if not rag_docs:
191
+ raise ValueError(f"Warning: No documents found in RAG for node {self.nuCore.url}. Skipping.")
192
+
193
+ for rag_doc in rag_docs:
194
+ device_docs += "\n" + rag_doc
195
+
196
+ sprompt = system_prompt.replace("{device_docs}", device_docs)
197
+ sprompt.strip()
198
+ with open(f"/tmp/ai.prompt", "w") as f:
199
+ f.write(sprompt)
200
+
201
+ system_message = {
202
+ "role": "system",
203
+ "content": sprompt
204
+ }
205
+ query= query.strip()
206
+ if not query:
207
+ await self.send_response("No query provided, exiting ...", True)
208
+ return None
209
+ if query.startswith("?"):
210
+ query = "\n"+query[1:].strip() # Remove leading '?' if presented
211
+ else:
212
+ # This is a code-only query, so we don't need to send the system prompt
213
+ query = f"**code-only** **no-explanation**\n{query}"
214
+
215
+ user_message = {
216
+ "role": "user",
217
+ "content": f"USER QUERY:{query}"
218
+ }
219
+
220
+ #first use rag for relevant documents
221
+ #rag_results = self.nuCore.query(query, num_rag_results, rerank)
222
+ #context = None
223
+ #if rag_results:
224
+ # context = "***Relevant documents***\n"
225
+ # for document in rag_results['documents']:
226
+ # context += f"---\n{document}"
227
+ #
228
+ # query = query.strip() if not context else f"{context.strip()}\n\n Customer Question: {query.strip()}"
229
+
230
+ # print (f"\n\n*********************Customer Query: {query}********************\n\n")
231
+
232
+ # if rag_results:
233
+ # print(f"\n\n*********************Top 5 Query Results:(Rerank = {rerank})********************\n\n")
234
+ # for i in range(len(rag_results['ids'])):
235
+ # print(f"{i+1}. {rag_results['ids'][i]} - {rag_results['distances'][i]} - {rag_results['relevance_scores'][i]}")
236
+ # print("\n\n***************************************************************\n\n")
237
+
238
+ #if not self.sent_system_prompt:
239
+ messages.append(system_message)
240
+ self.sent_system_prompt = True
241
+
242
+ messages.append(user_message)
243
+ # Step 1: Get tool call
244
+ payload={
245
+ "messages": messages,
246
+ "stream": True,
247
+ 'cache_prompt':True,
248
+ "n_keep": -1,
249
+ "temperature": 0.0,
250
+ "max_tokens": 60_000,
251
+ }
252
+ full_response = ""
253
+ try:
254
+ with httpx.stream("POST", self.__model_url__, timeout=100, json=payload,headers={
255
+ "Authorization": f"Bearer {self.__model_auth_token__}" if self.__model_auth_token__ else "",
256
+ }) as response:
257
+ if response.status_code == 401 or response.status_code == 403:
258
+ print(f"Authorization token is invalid or expired. You need to refresh it.")
259
+ return None
260
+ elif response.status_code == 500:
261
+ print(f"Internal server error. Please try again later (most probably the authorization token is invalid or expired).")
262
+ return None
263
+ else:
264
+ for line in response.iter_lines():
265
+ if line.startswith("data: "):
266
+ token_data = line[len(b"data: "):]
267
+ try:
268
+ finish_reason = json.loads(token_data.strip())['choices'][0]['finish_reason']
269
+ if finish_reason == "stop":
270
+ break
271
+ token_data = json.loads(token_data.strip())['choices'][0]['delta'].get('content', '')
272
+ except json.JSONDecodeError:
273
+ continue
274
+ if token_data:
275
+ # Print the token data as it arrives
276
+ if isinstance(token_data, bytes):
277
+ token_data = token_data.decode("utf-8")
278
+ await self.send_response(token_data, False)
279
+ #full_response += token_data # Collect the token data
280
+ full_response += token_data
281
+
282
+
283
+ # now parse the full response and look for blocks between __NUCORE_COMMAND_BEGIN__ and __NUCORE_COMMAND_END__.
284
+ # convert the blocks to json and add to list
285
+ await self.process_tool_call(full_response, "__BEGIN_NUCORE_COMMAND__", "__END_NUCORE_COMMAND__")
286
+ await self.process_tool_call(full_response, "__BEGIN_NUCORE_PROPERTY_QUERY__", "__END_NUCORE_PROPERTY_QUERY__")
287
+
288
+ except Exception as e:
289
+ print(f"An error occurred while processing the customer input: {e}")
290
+ return None
291
+
292
+
293
+ async def main(args):
294
+ print("Welcome to NuCore AI Assistant!")
295
+ print("Type 'quit' to exit")
296
+ assistant = NuCoreAssistant(args, websocket=None) # Replace with actual websocket connection if needed
297
+ i=0
298
+
299
+ while True:
300
+ try:
301
+ user_input = input("\nWhat can I do for you? > " if i==0 else "\n> ").strip()
302
+ i+=1
303
+
304
+ if not user_input:
305
+ print("Please enter a valid request")
306
+ continue
307
+
308
+ if user_input.lower() == 'quit':
309
+ print("Goodbye!")
310
+ break
311
+
312
+ print(f"\n>>>>>>>>>>\n")
313
+
314
+ await assistant.process_customer_input(user_input, num_rag_results=3, rerank=False)
315
+ print ("\n\n<<<<<<<<<<\n")
316
+
317
+ except Exception as e:
318
+ print(f"An error occurred: {e}")
319
+ continue
320
+
321
+ if __name__ == "__main__":
322
+ parser = argparse.ArgumentParser(
323
+ description="Loader for NuCore Profile and Nodes XML files."
324
+ )
325
+ parser.add_argument(
326
+ "--url",
327
+ dest="url",
328
+ type=str,
329
+ required=False,
330
+ help="The URL to fetch nodes and profiles from the nucore platform",
331
+ )
332
+ parser.add_argument(
333
+ "--username",
334
+ dest="username",
335
+ type=str,
336
+ required=False,
337
+ help="The username to authenticate with the nucore platform",
338
+ )
339
+ parser.add_argument(
340
+ "--password",
341
+ dest="password",
342
+ type=str,
343
+ required=False,
344
+ help="The password to authenticate with the nucore platform",
345
+ )
346
+ parser.add_argument(
347
+ "--collection_path",
348
+ dest="collection_path",
349
+ type=str,
350
+ required=False,
351
+ help="The path to the embedding collection db. If not provided, defaults to ~/.nucore_db.",
352
+ )
353
+ parser.add_argument(
354
+ "--model_url",
355
+ dest="model_url",
356
+ type=str,
357
+ required=False,
358
+ help="The URL of the remote model. If provided, this should be a valid URL that responds to OpenAI's API requests.",
359
+ )
360
+ parser.add_argument(
361
+ "--model_auth_token",
362
+ dest="model_auth_token",
363
+ type=str,
364
+ required=False,
365
+ help="Optional authentication token for the remote model API (if required by the remote model) to be used in the Authorization header. You are responsible for refreshing the token if needed.",
366
+ )
367
+
368
+
369
+ args = parser.parse_args()
370
+ asyncio.run(main(args))
371
+
372
+
@@ -0,0 +1,3 @@
1
+ from .config import AIConfig, LLAMA_CPP_EXECUTABLE, LLAMA_CPP_EXECUTABLE_WITH_GPU
2
+
3
+ __all__ = ["AIConfig", "LLAMA_CPP_EXECUTABLE", "LLAMA_CPP_EXECUTABLE_WITH_GPU"]
@@ -0,0 +1,68 @@
1
+ #configuration file for defaults
2
+ import os
3
+
4
+ DEFAULT_NUCORE_INSTALL_DIR = "workspace/nucore"
5
+
6
+ LLAMA_CPP_DIR = os.path.join(DEFAULT_NUCORE_INSTALL_DIR, "llama.cpp")
7
+ LLAMA_CPP_EXECUTABLE = os.path.join(os.path.expanduser("~"), LLAMA_CPP_DIR, "build.blis/bin/llama-server")
8
+ LLAMA_CPP_EXECUTABLE_WITH_GPU = os.path.join(os.path.expanduser("~"), LLAMA_CPP_DIR, "build.cuda/bin/llama-server")
9
+
10
+
11
+ class AIConfig:
12
+ def __init__(self, install_dir:str=None, models_path:str=None):
13
+ if not install_dir:
14
+ install_dir = os.path.join(os.path.expanduser('~'), DEFAULT_NUCORE_INSTALL_DIR)
15
+ self.__models_path__:str = models_path if models_path else os.path.join(install_dir, "models")
16
+
17
+ self.__ragdb_file__ = "ragdb"
18
+
19
+ self.__model_host__="localhost"
20
+ self.__model_port__=8013
21
+ self.__model_url__=f"http://{self.__model_host__}:{self.__model_port__}/v1/chat/completions"
22
+ self.__llm_model__ = "finetuned/qwen2.5-coder-dls-7b/qwen2.5-coder-nucore-7b-Q4_K_M.gguf"
23
+ self.__llm_model_params__ = "-c 60000 --temp 0.0 --repeat-penalty 1.1 --n-gpu-layers 100 --batch-size 8196"
24
+ self.__llm_model_server_args__ = f"-m {os.path.join(self.__models_path__,self.__llm_model__)} --host {self.__model_host__} --port {self.__model_port__} {self.__llm_model_params__}"
25
+
26
+ self.__reranker_host__="localhost"
27
+ self.__reranker_port__=8026
28
+ self.__reranker_url__=f"http://{self.__reranker_host__}:{self.__reranker_port__}/v1/rerank"
29
+ self.__reranker_model__ = "bge-reranker-v2-m3.gguf"
30
+ self.__reranker_model_params__ = "--reranking --temp 0.0 "
31
+ self.__reranker_model_server_args__ = f"-m {os.path.join(self.__models_path__,self.__reranker_model__)} --host {self.__reranker_host__} --port {self.__reranker_port__} {self.__reranker_model_params__}"
32
+
33
+ self.__embedding_host__="localhost"
34
+ self.__embedding_port__=8052
35
+ self.__embedding_url__=f"http://{self.__embedding_host__}:{self.__embedding_port__}/v1/embeddings"
36
+ self.__embedding_model__ = "Qwen3-Embedding-0.6B-f16.gguf"
37
+ self.__embedding_model_params__ = "--embeddings --pooling mean -ub 2048"
38
+ self.__embedding_model_server_args__ = f"-m {os.path.join(self.__models_path__,self.__embedding_model__)} --host {self.__embedding_host__} --port {self.__embedding_port__} {self.__embedding_model_params__}"
39
+
40
+
41
+ def getLLMModel(self, model:str=None):
42
+ if not model:
43
+ model = self.__llm_model__
44
+
45
+ return os.path.join(self.__models_path__, model)
46
+
47
+ def getRerankerModel(self, model:str=None):
48
+ if not model:
49
+ model = self.__llm_reranker_model__
50
+
51
+ return os.path.join(self.__models_path__, model)
52
+
53
+ def getEmbeddingModel(self, model:str=None):
54
+ if not model:
55
+ model = self.__llm_reranker_model__
56
+
57
+ return os.path.join(self.__models_path__, model)
58
+
59
+ def getModelURL(self):
60
+ return self.__model_url__
61
+
62
+ def getRerankerURL(self):
63
+ return self.__reranker_url__
64
+
65
+ def getEmbeddingURL(self):
66
+ return self.__embedding_url__
67
+
68
+
@@ -0,0 +1,17 @@
1
+ from .nucore_backend_api import NuCoreBackendAPI
2
+ from .nucore_programs import NuCorePrograms
3
+ from .cmd import Command, CommandParameter
4
+ from .editor import Editor, EditorMinMaxRange, EditorSubsetRange
5
+ from .linkdef import LinkDef, LinkParameter
6
+ from .node import Node, TypeInfo
7
+ from .nodedef import NodeDef, NodeProperty, NodeCommands, NodeLinks
8
+ from .profile import Profile, Family, Instance
9
+ from .uom import UOMEntry, get_uom_by_id
10
+ from .linkdef import LinkDef
11
+ from .node import Node
12
+ from .nodedef import NodeDef, NodeProperty, NodeCommands, NodeLinks, Property
13
+ from .profile import Profile, Family, Instance
14
+ from .uom import UOMEntry, get_uom_by_id
15
+ from .nucore import NuCore, NuCoreError
16
+
17
+ __all__ = ["NuCore", "EditorMinMaxRange", "TypeInfo", "LinkParameter", "Property", "EditorSubsetRange", "NuCoreError", "Node", "NuCoreBackendAPI", "NuCorePrograms", "Command", "CommandParameter", "Editor", "LinkDef", "NodeDef", "NodeProperty", "NodeCommands", "NodeLinks", "Profile", "Family", "Instance", "UOMEntry", get_uom_by_id]