Skip to content

Commit bda0565

Browse files
authored
Merge pull request #367 from weaviate/2.1.2
v2.1.2
2 parents d9e0582 + 20329c9 commit bda0565

File tree

9 files changed

+278
-86
lines changed

9 files changed

+278
-86
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [2.1.2] Adding Novita!
6+
7+
## Added
8+
9+
- Added Novita Generator (https://www.novita.ai/)
10+
- Added basic tests for Document class
11+
12+
## Fixed
13+
14+
- spaCy Language Issues (https://github.com/weaviate/Verba/issues/359#issuecomment-2612233766) (https://github.com/weaviate/Verba/issues/352)
15+
516
## [2.1.1] More Bugs!
617

718
## Added

CONTRIBUTING.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Open source is at the heart of Verba. We appreciate feedback, ideas, and enhance
88

99
## 📚 Before You Begin
1010

11-
Before contributing, please take a moment to read through the [README](https://github.com/weaviate/Verba/README.md) and the [Technical Documentation](https://github.com/weaviate/Verba/TECHNICAL.md). These documents provide a comprehensive understanding of the project and are essential reading to ensure that we're all on the same page.
11+
Before contributing, please take a moment to read through the [README](https://github.com/weaviate/Verba/README.md) and the [Technical Documentation](https://github.com/weaviate/Verba/TECHNICAL.md). These documents provide a comprehensive understanding of the project and are essential reading to ensure that we're all on the same page. Please note that the technical documentation is a work in progress and will be updated as we progress.
1212

1313
## 🐛 Reporting Issues
1414

@@ -22,6 +22,16 @@ If you've identified a bug or have an idea for an enhancement, please begin by c
2222

2323
We welcome all ideas and feedback. If you're not ready to open an Issue or if you're just looking for a place to discuss ideas, head over to our [GitHub Discussions](https://github.com/weaviate/Verba/discussions) or the [Weaviate Support Page](https://forum.weaviate.io/).
2424

25+
## 🧪 Testing
26+
27+
We use [pytest](https://docs.pytest.org) for testing. Please note that the tests are WIP and some are missing. We still encourage you to run the tests and add more tests as you see fit.
28+
29+
To run the tests, use the following command:
30+
31+
```bash
32+
pytest goldenverba/tests
33+
```
34+
2535
## 📝 Pull Requests
2636

2737
If you're ready to contribute code or documentation, please submit a Pull Request (PR) to the dev branch. Here's the process:
@@ -34,13 +44,6 @@ If you're ready to contribute code or documentation, please submit a Pull Reques
3444
- Include a clear description of your changes in the PR.
3545
- Link to the Issue in your PR description.
3646

37-
### 🧪 Tests and Formatting
38-
39-
To maintain the quality of the codebase, we ask that all contributors:
40-
41-
- Run unit tests to ensure that nothing is broken.
42-
- Use [Black](https://github.com/psf/black) to format your code before submitting.
43-
4447
### 🔄 Pull Request Process
4548

4649
- PRs are reviewed on a regular basis.

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pip install goldenverba
2525
- [OpenAI](#openai)
2626
- [HuggingFace](#huggingface)
2727
- [Groq](#groq)
28+
- [Novita AI](#novitaai)
2829
- [Quickstart: Deploy with pip](#how-to-deploy-with-pip)
2930
- [Quickstart: Build from Source](#how-to-build-from-source)
3031
- [Quickstart: Deploy with Docker](#how-to-install-verba-with-docker)
@@ -55,6 +56,7 @@ Verba is a fully-customizable personal assistant utilizing [Retrieval Augmented
5556
| Anthrophic (e.g. Claude Sonnet) || Embedding and Generation Models by Anthrophic |
5657
| OpenAI (e.g. GPT4) || Embedding and Generation Models by OpenAI |
5758
| Groq (e.g. Llama3) || Generation Models by Groq (LPU inference) |
59+
| Novita AI (e.g. Llama3.3) || Generation Models by Novita AI |
5860
| Upstage (e.g. Solar) || Embedding and Generation Models by Upstage |
5961

6062
| 🤖 Embedding Support | Implemented | Description |
@@ -168,6 +170,7 @@ Below is a comprehensive list of the API keys and variables you may require:
168170
| OPENAI_BASE_URL | URL to OpenAI instance | Models |
169171
| COHERE_API_KEY | Your API Key | Get Access to [Cohere](https://cohere.com/) Models |
170172
| GROQ_API_KEY | Your Groq API Key | Get Access to [Groq](https://groq.com/) Models |
173+
| NOVITA_API_KEY | Your Novita API Key | Get Access to [Novita AI](https://novita.ai?utm_source=github_verba&utm_medium=github_readme&utm_campaign=github_link) Models |
171174
| OLLAMA_URL | URL to your Ollama instance (e.g. http://localhost:11434 ) | Get Access to [Ollama](https://ollama.com/) Models |
172175
| UNSTRUCTURED_API_KEY | Your API Key | Get Access to [Unstructured](https://docs.unstructured.io/welcome) Data Ingestion |
173176
| UNSTRUCTURED_API_URL | URL to Unstructured Instance | Get Access to [Unstructured](https://docs.unstructured.io/welcome) Data Ingestion |
@@ -264,6 +267,11 @@ To use Groq LPUs as generation engine, you need to get an API key from [Groq](ht
264267
> Although you can provide it in the graphical interface when Verba is up, it is recommended to specify it as `GROQ_API_KEY` environment variable before you launch the application.
265268
> It will allow you to choose the generation model in an up-to-date available models list.
266269
270+
## Novita
271+
272+
To use Novita AI as generation engine, you need to get an API key from [Novita AI](https://novita.ai/settings/key-management?utm_source=github_verba&utm_medium=github_readme&utm_campaign=github_link).
273+
274+
267275
# How to deploy with pip
268276

269277
`Python >=3.10.0`

goldenverba/.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525

2626
# UPSTAGE_API_KEY=
2727

28+
# NOVITA_API_KEY=

goldenverba/components/document.py

Lines changed: 6 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,6 @@
77

88
from langdetect import detect
99

10-
SUPPORTED_LANGUAGES = {
11-
"en": "English",
12-
"zh": "Simplified Chinese",
13-
"zh-hant": "Traditional Chinese",
14-
"fr": "French",
15-
"de": "German",
16-
"nl": "Dutch",
17-
}
18-
1910

2011
def load_nlp_for_language(language: str):
2112
"""Load SpaCy models based on language"""
@@ -32,13 +23,10 @@ def load_nlp_for_language(language: str):
3223
elif language == "nl":
3324
nlp = spacy.blank("nl")
3425
else:
35-
raise ValueError(f"Unsupported language: {language}")
26+
nlp = spacy.blank("en")
27+
28+
nlp.add_pipe("sentencizer")
3629

37-
# Add sentence segmentation to languages
38-
if language == "en":
39-
nlp.add_pipe("sentencizer", config={"punct_chars": None})
40-
else:
41-
nlp.add_pipe("sentencizer") #
4230
return nlp
4331

4432

@@ -55,57 +43,6 @@ def detect_language(text: str) -> str:
5543
return "unknown"
5644

5745

58-
def split_text_by_language(text: str):
59-
"""Separate text into language parts based on character ranges"""
60-
chinese_simplified = "".join(
61-
[char for char in text if "\u4e00" <= char <= "\u9fff"]
62-
)
63-
chinese_traditional = "".join(
64-
[
65-
char
66-
for char in text
67-
if "\u3400" <= char <= "\u4dbf" or "\u4e00" <= char <= "\u9fff"
68-
]
69-
)
70-
english_part = "".join([char for char in text if char.isascii()])
71-
other_text = "".join(
72-
[char for char in text if not (char.isascii() or "\u4e00" <= char <= "\u9fff")]
73-
)
74-
75-
return chinese_simplified, chinese_traditional, english_part, other_text
76-
77-
78-
def process_mixed_language(content: str):
79-
"""Process mixed language text"""
80-
chinese_simplified, chinese_traditional, english_text, other_text = (
81-
split_text_by_language(content)
82-
)
83-
84-
docs = []
85-
86-
if chinese_simplified:
87-
nlp_zh = load_nlp_for_language("zh")
88-
docs.append(nlp_zh(chinese_simplified))
89-
90-
if chinese_traditional:
91-
nlp_zh_hant = load_nlp_for_language("zh-hant")
92-
docs.append(nlp_zh_hant(chinese_traditional))
93-
94-
if english_text:
95-
nlp_en = load_nlp_for_language("en")
96-
docs.append(nlp_en(english_text))
97-
98-
if other_text:
99-
detected_lang = detect_language(other_text)
100-
if detected_lang in SUPPORTED_LANGUAGES:
101-
nlp_other = load_nlp_for_language(detected_lang)
102-
docs.append(nlp_other(other_text))
103-
104-
# Merge all processed documents
105-
doc = Doc.from_docs(docs)
106-
return doc
107-
108-
10946
class Document:
11047
def __init__(
11148
self,
@@ -132,13 +69,9 @@ def __init__(
13269

13370
if len(content) > MAX_BATCH_SIZE:
13471
# Process content in batches
135-
print("TOOO BIG!")
13672
docs = []
13773
detected_language = detect_language(content[0:MAX_BATCH_SIZE])
138-
if detected_language in SUPPORTED_LANGUAGES:
139-
nlp = load_nlp_for_language(detected_language)
140-
else:
141-
nlp = process_mixed_language
74+
nlp = load_nlp_for_language(detected_language)
14275

14376
for i in range(0, len(content), MAX_BATCH_SIZE):
14477
docs.append(nlp(content[i : i + MAX_BATCH_SIZE]))
@@ -148,12 +81,8 @@ def __init__(
14881
else:
14982
# Process smaller content, directly based on language
15083
detected_language = detect_language(content)
151-
if detected_language in SUPPORTED_LANGUAGES:
152-
nlp = load_nlp_for_language(detected_language)
153-
doc = nlp(content)
154-
else:
155-
# Process mixed language content
156-
doc = process_mixed_language(content)
84+
nlp = load_nlp_for_language(detected_language)
85+
doc = nlp(content)
15786

15887
self.spacy_doc = doc
15988

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import os
2+
from dotenv import load_dotenv
3+
import json
4+
import aiohttp
5+
import requests
6+
7+
from goldenverba.components.interfaces import Generator
8+
from goldenverba.components.types import InputConfig
9+
from goldenverba.components.util import get_environment, get_token
10+
11+
load_dotenv()
12+
13+
base_url = "https://api.novita.ai/v3/openai"
14+
15+
16+
class NovitaGenerator(Generator):
17+
"""
18+
Novita Generator.
19+
"""
20+
21+
def __init__(self):
22+
super().__init__()
23+
self.name = "Novita AI"
24+
self.description = "Using Novita AI LLM models to generate answers to queries"
25+
self.context_window = 8192
26+
27+
models = get_models()
28+
29+
self.config["Model"] = InputConfig(
30+
type="dropdown",
31+
value=models[0],
32+
description="Select a Novita Model",
33+
values=models,
34+
)
35+
36+
if get_token("NOVITA_API_KEY") is None:
37+
self.config["API Key"] = InputConfig(
38+
type="password",
39+
value="",
40+
description="You can set your Novita API Key here or set it as environment variable `NOVITA_API_KEY`",
41+
values=[],
42+
)
43+
44+
async def generate_stream(
45+
self,
46+
config: dict,
47+
query: str,
48+
context: str,
49+
conversation: list[dict] = [],
50+
):
51+
system_message = config.get("System Message").value
52+
model = config.get("Model", {"value": "deepseek/deepseek_v3"}).value
53+
novita_key = get_environment(
54+
config, "API Key", "NOVITA_API_KEY", "No Novita API Key found"
55+
)
56+
novita_url = base_url
57+
58+
messages = self.prepare_messages(query, context, conversation, system_message)
59+
60+
headers = {
61+
"Content-Type": "application/json",
62+
"Authorization": f"Bearer {novita_key}",
63+
}
64+
data = {
65+
"messages": messages,
66+
"model": model,
67+
"stream": True,
68+
}
69+
70+
async with aiohttp.ClientSession() as client:
71+
async with client.post(
72+
url=f"{novita_url}/chat/completions",
73+
json=data,
74+
headers=headers,
75+
timeout=None,
76+
) as response:
77+
if response.status == 200:
78+
async for line in response.content:
79+
if line.strip():
80+
line = line.decode("utf-8").strip()
81+
if line == "data: [DONE]":
82+
yield {"message": "", "finish_reason": "stop"}
83+
else:
84+
if line.startswith("data:"):
85+
line = line[5:].strip()
86+
json_line = json.loads(line)
87+
choice = json_line.get("choices")[0]
88+
yield {
89+
"message": choice.get("delta", {}).get(
90+
"content", ""
91+
),
92+
"finish_reason": (
93+
"stop"
94+
if choice.get("finish_reason", "") == "stop"
95+
else ""
96+
),
97+
}
98+
else:
99+
error_message = await response.text()
100+
yield {
101+
"message": f"HTTP Error {response.status}: {error_message}",
102+
"finish_reason": "stop",
103+
}
104+
105+
def prepare_messages(
106+
self, query: str, context: str, conversation: list[dict], system_message: str
107+
) -> list[dict]:
108+
messages = [
109+
{
110+
"role": "system",
111+
"content": system_message,
112+
}
113+
]
114+
115+
for message in conversation:
116+
messages.append({"role": message.type, "content": message.content})
117+
118+
messages.append(
119+
{
120+
"role": "user",
121+
"content": f"Answer this query: '{query}' with this provided context: {context}",
122+
}
123+
)
124+
125+
return messages
126+
127+
128+
def get_models():
129+
try:
130+
response = requests.get(base_url + "/models")
131+
models = [model.get("id") for model in response.json().get("data")]
132+
if len(models) > 0:
133+
return models
134+
else:
135+
# msg.info("No Novita AI Model detected")
136+
return ["No Novita AI Model detected"]
137+
except Exception as e:
138+
# msg.fail(f"Couldn't connect to Novita AI: {e}")
139+
return [f"Couldn't connect to Novita AI"]

goldenverba/components/managers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
from goldenverba.components.generation.OllamaGenerator import OllamaGenerator
7171
from goldenverba.components.generation.OpenAIGenerator import OpenAIGenerator
7272
from goldenverba.components.generation.GroqGenerator import GroqGenerator
73+
from goldenverba.components.generation.NovitaGenerator import NovitaGenerator
7374
from goldenverba.components.generation.UpstageGenerator import UpstageGenerator
7475

7576
try:
@@ -116,6 +117,7 @@
116117
AnthropicGenerator(),
117118
CohereGenerator(),
118119
GroqGenerator(),
120+
NovitaGenerator(),
119121
UpstageGenerator(),
120122
]
121123
else:

0 commit comments

Comments
 (0)