feat: add integrations category in the sidebar (#3843)

* feat: Add skipFallback parameter to ForwardedIconComponent

This commit adds a new skipFallback parameter to the ForwardedIconComponent in the genericIconComponent module. The skipFallback parameter allows developers to skip rendering the fallback component when using Suspense. This can be useful in cases where the fallback component is not needed or should be handled differently.

* feat: Add skipFallback parameter to IconComponentProps

* feat: Add skipFallback parameter to IconComponentProps and ForwardedIconComponent

This commit adds the skipFallback parameter to the IconComponentProps and ForwardedIconComponent. This parameter allows skipping the fallback icon rendering when the specified icon is not found. The skipFallback parameter is used in the ParentDisclosureComponent to conditionally render the chevron-down or chevron-right icon based on the component's state.

The commit also updates the defaultOpen value in the ParentDisclosureComponent in the ExtraSidebarComponent. The defaultOpen value is set to true, ensuring that the "Integrations" section is always expanded when the component is rendered.

* refactor: Update ExtraSidebarComponent layout and styling

* [autofix.ci] apply automated fixes

* move file

* move file

* move file

* move file

* move file

* Update NotionLogo size

* Update size of AssemblyAI logo

* Update colors and names for AssemblyAI in styleUtils.ts

* Update BUNDLES_SIDEBAR_FOLDER_NAMES in constants.ts

* [autofix.ci] apply automated fixes

* add changes from pr #3934

* chore: add cache.db to .gitignore

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
anovazzi1 2024-10-02 16:07:57 -03:00 • committed by GitHub
commit 0d796db02c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 1483 additions and 45 deletions

1
.gitignore vendored
View file

@ -275,3 +275,4 @@ src/frontend/temp
.history
.dspy_cache/
cache.db

File diff suppressed because one or more lines are too long

View file

@ -45,7 +45,7 @@ This component allows you to submit an audio or video file for transcription.
- Audio File: The audio or video file to transcribe.
- Speech Model (Optional): Select the class of models. Default is *Best*. See [speech models](https://www.assemblyai.com/docs/speech-to-text/speech-recognition#select-the-speech-model-with-best-and-nano) for more info.
- Automatic Language Detection (Optional): Enable automatic language detection.
- Language (Optional): The language of the audio file. Can be set manually if automatic language detection is disabled.
- Language (Optional): The language of the audio file. Can be set manually if automatic language detection is disabled.
See [supported languages](https://www.assemblyai.com/docs/getting-started/supported-languages) for a list of supported language codes.
- Enable Speaker Labels (Optional): Detect speakers in an audio file and what each speaker said.
- Expected Number of Speakers (Optional): Set the expected number of speakers, if Speaker Labels is enabled.
@ -163,4 +163,3 @@ If you encounter issues:
4. Review the Langflow logs for any error messages.
For more advanced usage, refer to the [AssemblyAI API documentation](https://www.assemblyai.com/docs/). If you need more help, you can reach out to the [AssemblyAI support](https://www.assemblyai.com/contact/support).

View file

@ -0,0 +1,79 @@
import assemblyai as aai
from langflow.custom import Component
from langflow.io import DataInput, DropdownInput, IntInput, Output, SecretStrInput
from langflow.schema import Data
class AssemblyAIGetSubtitles(Component):
display_name = "AssemblyAI Get Subtitles"
description = "Export your transcript in SRT or VTT format for subtitles and closed captions"
documentation = "https://www.assemblyai.com/docs"
icon = "AssemblyAI"
inputs = [
SecretStrInput(
name="api_key",
display_name="Assembly API Key",
info="Your AssemblyAI API key. You can get one from https://www.assemblyai.com/",
),
DataInput(
name="transcription_result",
display_name="Transcription Result",
info="The transcription result from AssemblyAI",
),
DropdownInput(
name="subtitle_format",
display_name="Subtitle Format",
options=["srt", "vtt"],
value="srt",
info="The format of the captions (SRT or VTT)",
),
IntInput(
name="chars_per_caption",
display_name="Characters per Caption",
info="The maximum number of characters per caption (0 for no limit)",
value=0,
advanced=True,
),
]
outputs = [
Output(display_name="Subtitles", name="subtitles", method="get_subtitles"),
]
def get_subtitles(self) -> Data:
aai.settings.api_key = self.api_key
# check if it's an error message from the previous step
if self.transcription_result.data.get("error"):
self.status = self.transcription_result.data["error"]
return self.transcription_result
try:
transcript_id = self.transcription_result.data["id"]
transcript = aai.Transcript.get_by_id(transcript_id)
except Exception as e:
error = f"Getting transcription failed: {str(e)}"
self.status = error
return Data(data={"error": error})
if transcript.status == aai.TranscriptStatus.completed:
subtitles = None
chars_per_caption = self.chars_per_caption if self.chars_per_caption > 0 else None
if self.subtitle_format == "srt":
subtitles = transcript.export_subtitles_srt(chars_per_caption)
else:
subtitles = transcript.export_subtitles_vtt(chars_per_caption)
result = Data(
subtitles=subtitles,
format=self.subtitle_format,
transcript_id=transcript_id,
chars_per_caption=chars_per_caption,
)
self.status = result
return result
self.status = transcript.error
return Data(data={"error": transcript.error})

View file

@ -0,0 +1,182 @@
import assemblyai as aai
from langflow.custom import Component
from langflow.io import DataInput, DropdownInput, FloatInput, IntInput, MultilineInput, Output, SecretStrInput
from langflow.schema import Data
class AssemblyAILeMUR(Component):
display_name = "AssemblyAI LeMUR"
description = "Apply Large Language Models to spoken data using the AssemblyAI LeMUR framework"
documentation = "https://www.assemblyai.com/docs/lemur"
icon = "AssemblyAI"
inputs = [
SecretStrInput(
name="api_key",
display_name="Assembly API Key",
info="Your AssemblyAI API key. You can get one from https://www.assemblyai.com/",
advanced=False,
),
DataInput(
name="transcription_result",
display_name="Transcription Result",
info="The transcription result from AssemblyAI",
),
MultilineInput(
name="prompt",
display_name="Input Prompt",
info="The text to prompt the model",
),
DropdownInput(
name="final_model",
display_name="Final Model",
options=["claude3_5_sonnet", "claude3_opus", "claude3_haiku", "claude3_sonnet"],
value="claude3_5_sonnet",
info="The model that is used for the final prompt after compression is performed",
advanced=True,
),
FloatInput(
name="temperature",
display_name="Temperature",
advanced=True,
value=0.0,
info="The temperature to use for the model",
),
IntInput(
name="max_output_size",
display_name=" Max Output Size",
advanced=True,
value=2000,
info="Max output size in tokens, up to 4000",
),
DropdownInput(
name="endpoint",
display_name="Endpoint",
options=["task", "summary", "question-answer"],
value="task",
info=(
"The LeMUR endpoint to use. For 'summary' and 'question-answer',"
" no prompt input is needed. See https://www.assemblyai.com/docs/api-reference/lemur/ for more info."
),
advanced=True,
),
MultilineInput(
name="questions",
display_name="Questions",
info="Comma-separated list of your questions. Only used if Endpoint is 'question-answer'",
advanced=True,
),
MultilineInput(
name="transcript_ids",
display_name="Transcript IDs",
info=(
"Comma-separated list of transcript IDs. LeMUR can perform actions over multiple transcripts."
" If provided, the Transcription Result is ignored."
),
advanced=True,
),
]
outputs = [
Output(display_name="LeMUR Response", name="lemur_response", method="run_lemur"),
]
def run_lemur(self) -> Data:
"""Use the LeMUR task endpoint to input the LLM prompt."""
aai.settings.api_key = self.api_key
if not self.transcription_result and not self.transcript_ids:
error = "Either a Transcription Result or Transcript IDs must be provided"
self.status = error
return Data(data={"error": error})
if self.transcription_result and self.transcription_result.data.get("error"):
# error message from the previous step
self.status = self.transcription_result.data["error"]
return self.transcription_result
if self.endpoint == "task" and not self.prompt:
self.status = "No prompt specified for the task endpoint"
return Data(data={"error": "No prompt specified"})
if self.endpoint == "question-answer" and not self.questions:
error = "No Questions were provided for the question-answer endpoint"
self.status = error
return Data(data={"error": error})
# Check for valid transcripts
transcript_ids = None
if self.transcription_result and "id" in self.transcription_result.data:
transcript_ids = [self.transcription_result.data["id"]]
elif self.transcript_ids:
transcript_ids = self.transcript_ids.split(",") or []
transcript_ids = [t.strip() for t in transcript_ids]
if not transcript_ids:
error = "Either a valid Transcription Result or valid Transcript IDs must be provided"
self.status = error
return Data(data={"error": error})
# Get TranscriptGroup and check if there is any error
transcript_group = aai.TranscriptGroup(transcript_ids=transcript_ids)
transcript_group, failures = transcript_group.wait_for_completion(return_failures=True)
if failures:
error = f"Getting transcriptions failed: {failures[0]}"
self.status = error
return Data(data={"error": error})
for t in transcript_group.transcripts:
if t.status == aai.TranscriptStatus.error:
self.status = t.error
return Data(data={"error": t.error})
# Perform LeMUR action
try:
response = self.perform_lemur_action(transcript_group, self.endpoint)
result = Data(data=response)
self.status = result
return result
except Exception as e:
error = f"An Error happened: {str(e)}"
self.status = error
return Data(data={"error": error})
def perform_lemur_action(self, transcript_group: aai.TranscriptGroup, endpoint: str) -> dict:
print("Endpoint:", endpoint, type(endpoint))
if endpoint == "task":
result = transcript_group.lemur.task(
prompt=self.prompt,
final_model=self.get_final_model(self.final_model),
temperature=self.temperature,
max_output_size=self.max_output_size,
)
elif endpoint == "summary":
result = transcript_group.lemur.summarize(
final_model=self.get_final_model(self.final_model),
temperature=self.temperature,
max_output_size=self.max_output_size,
)
elif endpoint == "question-answer":
questions = self.questions.split(",")
questions = [aai.LemurQuestion(question=q) for q in questions]
result = transcript_group.lemur.question(
questions=questions,
final_model=self.get_final_model(self.final_model),
temperature=self.temperature,
max_output_size=self.max_output_size,
)
else:
msg = f"Endpoint not supported: {endpoint}"
raise ValueError(msg)
return result.dict()
def get_final_model(self, model_name: str) -> aai.LemurModel:
if model_name == "claude3_5_sonnet":
return aai.LemurModel.claude3_5_sonnet
if model_name == "claude3_opus":
return aai.LemurModel.claude3_opus
if model_name == "claude3_haiku":
return aai.LemurModel.claude3_haiku
if model_name == "claude3_sonnet":
return aai.LemurModel.claude3_sonnet
msg = f"Model name not supported: {model_name}"
raise ValueError(msg)

View file

@ -0,0 +1,91 @@
import assemblyai as aai
from langflow.custom import Component
from langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SecretStrInput
from langflow.schema import Data
class AssemblyAIListTranscripts(Component):
display_name = "AssemblyAI List Transcripts"
description = "Retrieve a list of transcripts from AssemblyAI with filtering options"
documentation = "https://www.assemblyai.com/docs"
icon = "AssemblyAI"
inputs = [
SecretStrInput(
name="api_key",
display_name="Assembly API Key",
info="Your AssemblyAI API key. You can get one from https://www.assemblyai.com/",
),
IntInput(
name="limit",
display_name="Limit",
info="Maximum number of transcripts to retrieve (default: 20, use 0 for all)",
value=20,
),
DropdownInput(
name="status_filter",
display_name="Status Filter",
options=["all", "queued", "processing", "completed", "error"],
value="all",
info="Filter by transcript status",
advanced=True,
),
MessageTextInput(
name="created_on",
display_name="Created On",
info="Only get transcripts created on this date (YYYY-MM-DD)",
advanced=True,
),
BoolInput(
name="throttled_only",
display_name="Throttled Only",
info="Only get throttled transcripts, overrides the status filter",
advanced=True,
),
]
outputs = [
Output(display_name="Transcript List", name="transcript_list", method="list_transcripts"),
]
def list_transcripts(self) -> list[Data]:
aai.settings.api_key = self.api_key
params = aai.ListTranscriptParameters()
if self.limit:
params.limit = self.limit
if self.status_filter != "all":
params.status = self.status_filter
if self.created_on and self.created_on.text:
params.created_on = self.created_on.text
if self.throttled_only:
params.throttled_only = True
try:
transcriber = aai.Transcriber()
def convert_page_to_data_list(page):
return [Data(**t.dict()) for t in page.transcripts]
if self.limit == 0:
# paginate over all pages
params.limit = 100
page = transcriber.list_transcripts(params)
transcripts = convert_page_to_data_list(page)
while page.page_details.before_id_of_prev_url is not None:
params.before_id = page.page_details.before_id_of_prev_url
page = transcriber.list_transcripts(params)
transcripts.extend(convert_page_to_data_list(page))
else:
# just one page
page = transcriber.list_transcripts(params)
transcripts = convert_page_to_data_list(page)
self.status = transcripts
return transcripts
except Exception as e:
error_data = Data(data={"error": f"An error occurred: {str(e)}"})
self.status = [error_data]
return [error_data]

View file

@ -0,0 +1,66 @@
import assemblyai as aai
from langflow.custom import Component
from langflow.io import DataInput, FloatInput, Output, SecretStrInput
from langflow.schema import Data
class AssemblyAITranscriptionJobPoller(Component):
display_name = "AssemblyAI Poll Transcript"
description = "Poll for the status of a transcription job using AssemblyAI"
documentation = "https://www.assemblyai.com/docs"
icon = "AssemblyAI"
inputs = [
SecretStrInput(
name="api_key",
display_name="Assembly API Key",
info="Your AssemblyAI API key. You can get one from https://www.assemblyai.com/",
),
DataInput(
name="transcript_id",
display_name="Transcript ID",
info="The ID of the transcription job to poll",
),
FloatInput(
name="polling_interval",
display_name="Polling Interval",
value=3.0,
info="The polling interval in seconds",
advanced=True,
),
]
outputs = [
Output(display_name="Transcription Result", name="transcription_result", method="poll_transcription_job"),
]
def poll_transcription_job(self) -> Data:
"""Polls the transcription status until completion and returns the Data."""
aai.settings.api_key = self.api_key
aai.settings.polling_interval = self.polling_interval
# check if it's an error message from the previous step
if self.transcript_id.data.get("error"):
self.status = self.transcript_id.data["error"]
return self.transcript_id
try:
transcript = aai.Transcript.get_by_id(self.transcript_id.data["transcript_id"])
except Exception as e:
error = f"Getting transcription failed: {str(e)}"
self.status = error
return Data(data={"error": error})
if transcript.status == aai.TranscriptStatus.completed:
json_response = transcript.json_response
text = json_response.pop("text", None)
utterances = json_response.pop("utterances", None)
transcript_id = json_response.pop("id", None)
sorted_data = {"text": text, "utterances": utterances, "id": transcript_id}
sorted_data.update(json_response)
data = Data(data=sorted_data)
self.status = data
return data
self.status = transcript.error
return Data(data={"error": transcript.error})

View file

@ -0,0 +1,185 @@
import os
import assemblyai as aai
from loguru import logger
from langflow.custom import Component
from langflow.io import BoolInput, DropdownInput, FileInput, MessageTextInput, Output, SecretStrInput
from langflow.schema import Data
class AssemblyAITranscriptionJobCreator(Component):
display_name = "AssemblyAI Start Transcript"
description = "Create a transcription job for an audio file using AssemblyAI with advanced options"
documentation = "https://www.assemblyai.com/docs"
icon = "AssemblyAI"
inputs = [
SecretStrInput(
name="api_key",
display_name="Assembly API Key",
info="Your AssemblyAI API key. You can get one from https://www.assemblyai.com/",
),
FileInput(
name="audio_file",
display_name="Audio File",
file_types=[
"3ga",
"8svx",
"aac",
"ac3",
"aif",
"aiff",
"alac",
"amr",
"ape",
"au",
"dss",
"flac",
"flv",
"m4a",
"m4b",
"m4p",
"m4r",
"mp3",
"mpga",
"ogg",
"oga",
"mogg",
"opus",
"qcp",
"tta",
"voc",
"wav",
"wma",
"wv",
"webm",
"mts",
"m2ts",
"ts",
"mov",
"mp2",
"mp4",
"m4p",
"m4v",
"mxf",
],
info="The audio file to transcribe",
),
MessageTextInput(
name="audio_file_url",
display_name="Audio File URL",
info="The URL of the audio file to transcribe (Can be used instead of a File)",
advanced=True,
),
DropdownInput(
name="speech_model",
display_name="Speech Model",
options=[
"best",
"nano",
],
value="best",
info="The speech model to use for the transcription",
advanced=True,
),
BoolInput(
name="language_detection",
display_name="Automatic Language Detection",
info="Enable automatic language detection",
advanced=True,
),
MessageTextInput(
name="language_code",
display_name="Language",
info=(
"""
The language of the audio file. Can be set manually if automatic language detection is disabled.
See https://www.assemblyai.com/docs/getting-started/supported-languages """
"for a list of supported language codes."
),
advanced=True,
),
BoolInput(
name="speaker_labels",
display_name="Enable Speaker Labels",
info="Enable speaker diarization",
),
MessageTextInput(
name="speakers_expected",
display_name="Expected Number of Speakers",
info="Set the expected number of speakers (optional, enter a number)",
advanced=True,
),
BoolInput(
name="punctuate",
display_name="Punctuate",
info="Enable automatic punctuation",
advanced=True,
value=True,
),
BoolInput(
name="format_text",
display_name="Format Text",
info="Enable text formatting",
advanced=True,
value=True,
),
]
outputs = [
Output(display_name="Transcript ID", name="transcript_id", method="create_transcription_job"),
]
def create_transcription_job(self) -> Data:
aai.settings.api_key = self.api_key
# Convert speakers_expected to int if it's not empty
speakers_expected = None
if self.speakers_expected and self.speakers_expected.strip():
try:
speakers_expected = int(self.speakers_expected)
except ValueError:
self.status = "Error: Expected Number of Speakers must be a valid integer"
return Data(data={"error": "Error: Expected Number of Speakers must be a valid integer"})
language_code = self.language_code if self.language_code else None
config = aai.TranscriptionConfig(
speech_model=self.speech_model,
language_detection=self.language_detection,
language_code=language_code,
speaker_labels=self.speaker_labels,
speakers_expected=speakers_expected,
punctuate=self.punctuate,
format_text=self.format_text,
)
audio = None
if self.audio_file:
if self.audio_file_url:
logger.warning("Both an audio file an audio URL were specified. The audio URL was ignored.")
# Check if the file exists
if not os.path.exists(self.audio_file):
self.status = "Error: Audio file not found"
return Data(data={"error": "Error: Audio file not found"})
audio = self.audio_file
elif self.audio_file_url:
audio = self.audio_file_url
else:
self.status = "Error: Either an audio file or an audio URL must be specified"
return Data(data={"error": "Error: Either an audio file or an audio URL must be specified"})
try:
transcript = aai.Transcriber().submit(audio, config=config)
if transcript.error:
self.status = transcript.error
return Data(data={"error": transcript.error})
result = Data(data={"transcript_id": transcript.id})
self.status = result
return result
except Exception as e:
self.status = f"An error occurred: {str(e)}"
return Data(data={"error": f"An error occurred: {str(e)}"})

View file

@ -125,7 +125,6 @@ pytest-flakefinder = "^1.1.0"
types-markdown = "^3.7.0.20240822"
assemblyai = "^0.33.0"
[tool.pytest.ini_options]
minversion = "6.0"
#addopts = "-ra"

View file

@ -17,6 +17,7 @@ export const ForwardedIconComponent = memo(
stroke,
strokeWidth,
id = "",
skipFallback = false,
}: IconComponentProps,
ref,
) => {
@ -56,7 +57,7 @@ export const ForwardedIconComponent = memo(
);
return (
<Suspense fallback={fallback}>
<Suspense fallback={skipFallback ? undefined : fallback}>
<TargetIcon
className={className}
style={style}

View file

@ -729,7 +729,12 @@ export const PRIORITY_SIDEBAR_ORDER = [
"embeddings",
];
export const BUNDLES_SIDEBAR_FOLDER_NAMES = ["notion", "Notion"];
export const BUNDLES_SIDEBAR_FOLDER_NAMES = [
"notion",
"Notion",
"AssemblyAI",
"assemblyai",
];
export const AUTHORIZED_DUPLICATE_REQUESTS = [
"/health",

View file

@ -1,7 +1,7 @@
const AssemblyAISVG = (props) => (
<svg
width="501"
height="434"
width="1.1em"
height="1.1em"
viewBox="0 0 501 434"
fill="none"
xmlns="http://www.w3.org/2000/svg"

View file

@ -1,8 +1,8 @@
const SvgNotionLogo = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
width="1.1em"
height="1.1em"
fill="none"
viewBox="0 0 100 100"
{...props}

View file

@ -33,7 +33,8 @@ export default function ParentDisclosureComponent({
))}
<div>
<IconComponent
name="ChevronsUpDownIcon"
skipFallback
name={open ? "chevron-down" : "chevron-right"}
className={`${
open || defaultOpen ? "" : ""
} h-4 w-4 text-foreground`}

View file

@ -245,6 +245,35 @@ export default function ExtraSidebar(): JSX.Element {
<div key={index}></div>
),
)}
<>
<ParentDisclosureComponent
defaultOpen={true}
key={`${search.length !== 0}-${getFilterEdge.length !== 0}-Bundle`}
button={{
title: "Integrations",
Icon: nodeIconsLucide.unknown,
}}
testId="bundle-extended-disclosure"
>
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => BUNDLES_SIDEBAR_FOLDER_NAMES.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<SidebarCategoryComponent
key={`DisclosureComponent${index + search + JSON.stringify(getFilterEdge)}`}
search={search}
getFilterEdge={getFilterEdge}
category={dataFilter[SBSectionName]}
name={SBSectionName}
onDragStart={onDragStart}
/>
) : (
<div key={index}></div>
),
)}
</ParentDisclosureComponent>
</>
<ParentDisclosureComponent
defaultOpen={search.length !== 0 || getFilterEdge.length !== 0}
key={`${search.length !== 0}-${getFilterEdge.length !== 0}-Advanced`}
@ -306,39 +335,6 @@ export default function ExtraSidebar(): JSX.Element {
</a>
)}
</ParentDisclosureComponent>
{ENABLE_MVPS && (
<>
<Separator />
<ParentDisclosureComponent
defaultOpen={search.length !== 0 || getFilterEdge.length !== 0}
key={`${search.length !== 0}-${getFilterEdge.length !== 0}-Bundle`}
button={{
title: "Bundles",
Icon: nodeIconsLucide.unknown,
}}
testId="bundle-extended-disclosure"
>
{Object.keys(dataFilter)
.sort(sortKeys)
.filter((x) => BUNDLES_SIDEBAR_FOLDER_NAMES.includes(x))
.map((SBSectionName: keyof APIObjectType, index) =>
Object.keys(dataFilter[SBSectionName]).length > 0 ? (
<SidebarCategoryComponent
key={`DisclosureComponent${index + search + JSON.stringify(getFilterEdge)}`}
search={search}
getFilterEdge={getFilterEdge}
category={dataFilter[SBSectionName]}
name={SBSectionName}
onDragStart={onDragStart}
/>
) : (
<div key={index}></div>
),
)}
</ParentDisclosureComponent>
</>
)}
</div>
</div>
);

View file

@ -231,7 +231,7 @@
@apply fill-chat-trigger-disabled stroke-chat-trigger-disabled stroke-1;
}
.parent-disclosure-arrangement {
@apply flex w-full select-none items-center justify-between bg-background px-5 py-3;
@apply flex w-full select-none items-center justify-between bg-background pl-5 pr-3 py-3;
}
.components-disclosure-arrangement {
@apply -mt-px flex w-full select-none items-center justify-between border-y border-y-input bg-muted px-3 py-2;

View file

@ -369,6 +369,7 @@ export type IconComponentProps = {
stroke?: string;
strokeWidth?: number;
id?: string;
skipFallback?: boolean;
};
export type InputProps = {

View file

@ -297,6 +297,8 @@ export const nodeColors: { [char: string]: string } = {
wrappers: "#E6277A",
notion: "#000000",
Notion: "#000000",
AssemblyAI: "#213ED7",
assemblyai: "#213ED7",
helpers: "#31A3CC",
prototypes: "#E6277A",
astra_assistants: "#272541",
@ -325,6 +327,8 @@ export const nodeNames: { [char: string]: string } = {
models: "Models",
notion: "Notion",
Notion: "Notion",
AssemblyAI: "AssemblyAI",
assemblyai: "AssemblyAI",
model_specs: "Model Specs",
chains: "Chains",
agents: "Agents",
@ -391,6 +395,7 @@ export const nodeIconsLucide: iconsType = {
Amazon: AWSIcon,
Anthropic: AnthropicIcon,
ChatAnthropic: AnthropicIcon,
assemblyai: AssemblyAIIcon,
AssemblyAI: AssemblyAIIcon,
AstraDB: AstraDBIcon,
BingSearchAPIWrapper: BingIcon,