Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions api/oss/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,19 @@ async def _get_blocked_emails() -> Set[str]:

async def _is_blocked(email: str) -> bool:
email = email.lower()
if email in await _get_blocked_emails():
domain = email.split("@")[-1] if "@" in email else ""
allowed_domains = env.AGENTA_ALLOWED_DOMAINS
is_domain_allowed = allowed_domains and domain in allowed_domains

if allowed_domains and not is_domain_allowed:
return True
if "@" in email and email.split("@")[-1] in await _get_blocked_domains():

if email and email in await _get_blocked_emails():
return True

if domain and domain in await _get_blocked_domains() and not is_domain_allowed:
return True

return False


Expand Down
5 changes: 5 additions & 0 deletions api/oss/src/utils/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ class EnvironSettings(BaseModel):
for e in (os.getenv("AGENTA_BLOCKED_DOMAINS") or "").split(",")
if e.strip()
}
AGENTA_ALLOWED_DOMAINS: set = {
e.strip().lower()
for e in (os.getenv("AGENTA_ALLOWED_DOMAINS") or "").split(",")
if e.strip()
}

# AGENTA-SPECIFIC (INTERNAL INFRA)
DOCKER_NETWORK_MODE: str = os.getenv("DOCKER_NETWORK_MODE") or "bridge"
Expand Down
2 changes: 1 addition & 1 deletion api/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "api"
version = "0.59.10"
version = "0.59.11"
description = "Agenta API"
authors = [
{ name = "Mahmoud Mabrouk", email = "[email protected]" },
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/self-host/02-configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Optional Agenta-specific configurations:
| `AGENTA_SEND_EMAIL_FROM_ADDRESS` | From address for system emails | `[email protected]` |
| `AGENTA_API_INTERNAL_URL` | Internal API URL for services | _(empty)_ |
| `AGENTA_SERVICE_MIDDLEWARE_CACHE_ENABLED` | Enable middleware caching in the chat and completion services | `true` |
| `AGENTA_ALLOWED_DOMAINS` | Comma-separated list of email domains allowed to authenticate; when set, all other domains are rejected | _(empty)_ |
| `AGENTA_OTLP_MAX_BATCH_BYTES` | Max OTLP batch size before requests are rejected with 413 | `10485760` (10MB) |

### Third-party (Required)
Expand Down Expand Up @@ -238,4 +239,3 @@ TRAEFIK_PORT=80
For configuration assistance:
- Check the [GitHub issues](https://github.com/Agenta-AI/agenta/issues)
- Join our [Slack community](https://join.slack.com/t/agenta-hq/shared_invite/zt-37pnbp5s6-mbBrPL863d_oLB61GSNFjw)
- Review the deployment documentation for your specific setup
2 changes: 1 addition & 1 deletion sdk/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "agenta"
version = "0.59.10"
version = "0.59.11"
description = "The SDK for agenta is an open-source LLMOps platform."
readme = "README.md"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion web/ee/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agenta/ee",
"version": "0.59.10",
"version": "0.59.11",
"private": true,
"engines": {
"node": ">=18"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ const ConfigureEvaluatorPage = ({evaluatorId}: {evaluatorId?: string | null}) =>
const handleSuccess = useCallback(async () => {
message.success("Evaluator configuration saved")
await refetchAll()
await navigateBack()
}, [navigateBack, refetchAll])
}, [refetchAll])

if (!router.isReady || isLoading) {
return <ConfigureEvaluatorSkeleton />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {useCallback} from "react"

import {InfoCircleOutlined} from "@ant-design/icons"
import Editor from "@monaco-editor/react"
import {theme, Form, Tooltip, InputNumber, Switch, Input, AutoComplete} from "antd"
import {Rule} from "antd/es/form"
import {FormInstance, Rule} from "antd/es/form"
import Link from "next/link"
import {createUseStyles} from "react-jss"

import {useAppTheme} from "@/oss/components/Layout/ThemeContextProvider"
import SharedEditor from "@/oss/components/Playground/Components/SharedEditor"
import {isValidRegex} from "@/oss/lib/helpers/validators"
import {generatePaths} from "@/oss/lib/transformers"
import {EvaluationSettingsTemplate, JSSTheme} from "@/oss/lib/Types"
Expand All @@ -15,15 +16,24 @@ import {Messages} from "./Messages"
type DynamicFormFieldProps = EvaluationSettingsTemplate & {
name: string | string[]
traceTree: Record<string, any>
form?: FormInstance<any>
}

const useStyles = createUseStyles((theme: JSSTheme) => ({
editor: {
border: `1px solid ${theme.colorBorder}`,
borderRadius: theme.borderRadius,
overflow: "hidden",
"& .monaco-editor": {
width: "0 !important",
codeEditor: {
"& .agenta-editor-wrapper": {
minHeight: 375,
},
"&.agenta-shared-editor": {
borderColor: theme.colorBorder,
},
},
objectEditor: {
"& .agenta-editor-wrapper": {
minHeight: 120,
},
"&.agenta-shared-editor": {
borderColor: theme.colorBorder,
},
},
ExternalHelp: {
Expand All @@ -45,6 +55,41 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({
},
}))

interface ControlledSharedEditorProps {
value?: unknown
onChange?: (value: string) => void
className?: string
language?: "json" | "yaml" | "code"
}

const ControlledSharedEditor = ({
value,
onChange,
className,
language,
}: ControlledSharedEditorProps) => {
const handleValueChange = useCallback(
(next: string) => {
onChange?.(next)
},
[onChange],
)

return (
<SharedEditor
initialValue={value}
value={value as string}
handleChange={handleValueChange}
className={className}
syncWithInitialValueChanges
editorProps={{
codeOnly: true,
...(language ? {language} : {}),
}}
/>
)
}

export const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
name,
label,
Expand All @@ -55,11 +100,24 @@ export const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
max,
required,
traceTree,
form,
}) => {
const {appTheme} = useAppTheme()
const settingsValue = Form.useWatch(name, form)

const classes = useStyles()
const {token} = theme.useToken()

const handleValueChange = useCallback(
(next: string) => {
if (form) {
form.setFieldsValue({
[name as string]: next,
})
}
},
[form, name],
)

const rules: Rule[] = [{required: required ?? true, message: "This field is required"}]
if (type === "regex")
rules.push({
Expand Down Expand Up @@ -100,7 +158,11 @@ export const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
)}
</div>
}
initialValue={defaultVal}
initialValue={
type === "object" && defaultVal && typeof defaultVal === "object"
? JSON.stringify(defaultVal, null, 2)
: defaultVal
}
rules={rules}
hidden={type === "hidden"}
>
Expand All @@ -126,21 +188,18 @@ export const DynamicFormField: React.FC<DynamicFormFieldProps> = ({
) : type === "text" ? (
<Input.TextArea rows={10} />
) : type === "code" ? (
<Editor
className={classes.editor}
height={375}
width="100%"
language="python"
theme={`vs-${appTheme}`}
<ControlledSharedEditor
className={classes.codeEditor}
value={settingsValue}
onChange={handleValueChange}
language="code"
/>
) : type === "object" ? (
<Editor
className={classes.editor}
height={120}
width="100%"
<ControlledSharedEditor
className={classes.objectEditor}
language="json"
options={{lineNumbers: "off"}}
theme={`vs-${appTheme}`}
value={settingsValue}
onChange={handleValueChange}
/>
) : null}
</Form.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ const ConfigureEvaluator = ({
{...field}
key={field.key}
traceTree={traceTree}
form={form}
name={["settings_values", field.key]}
/>
))}
Expand Down
2 changes: 1 addition & 1 deletion web/oss/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@agenta/oss",
"version": "0.59.10",
"version": "0.59.11",
"private": true,
"engines": {
"node": ">=18"
Expand Down
17 changes: 14 additions & 3 deletions web/oss/src/components/DynamicCodeBlock/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,16 @@ const theme: EditorThemeClasses = {
}

// Normalize language ids using Lexical's helper
const normalizeShikiLang = (lang: string) => normalizeCodeLanguage((lang || "").toLowerCase())
const LANGUAGE_FALLBACKS: Record<string, string> = {
code: "python",
}

const resolveLexicalLanguage = (language: string): string => {
const normalized = (language || "").toLowerCase()
const fallback = LANGUAGE_FALLBACKS[normalized] ?? normalized
const resolved = normalizeCodeLanguage(fallback)
return resolved || "plaintext"
}

const ShikiHighlightPlugin: FC<{langs: string[]; themeName: string}> = ({langs, themeName}) => {
const [editor] = useLexicalComposerContext()
Expand Down Expand Up @@ -97,6 +106,8 @@ const InitializeContentPlugin: FC<{language: string; value: string}> = ({languag
const CodeBlock: FC<CodeBlockProps> = ({language, value}) => {
const classes = useStyles()

const lexicalLanguage = useMemo(() => resolveLexicalLanguage(language), [language])

const editorConfig = useMemo(
() => ({
namespace: "AgentaCodeBlock",
Expand All @@ -109,7 +120,7 @@ const CodeBlock: FC<CodeBlockProps> = ({language, value}) => {
)

const shikiTheme = "github-light"
const shikiLang = useMemo(() => normalizeShikiLang(language), [language])
const shikiLang = lexicalLanguage
const langs = useMemo(() => [shikiLang], [shikiLang])

return (
Expand All @@ -120,7 +131,7 @@ const CodeBlock: FC<CodeBlockProps> = ({language, value}) => {
placeholder={null}
ErrorBoundary={LexicalErrorBoundary}
/>
<InitializeContentPlugin language={shikiLang} value={value} />
<InitializeContentPlugin language={lexicalLanguage} value={value} />
<ShikiHighlightPlugin langs={langs} themeName={shikiTheme} />
</LexicalComposer>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

import {createCommand, LexicalCommand} from "lexical"

import type {CodeLanguage} from "../plugins/code/types"

export interface InitialContentPayload {
/** The initial content to be processed */
content: string
/** The language for syntax highlighting */
language: "json" | "yaml"
language: CodeLanguage
/** Whether this content should be handled by the default plugin */
preventDefault: () => void
/** Whether default handling has been prevented */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@

import {ElementNode, LexicalNode, SerializedElementNode, Spread, EditorConfig} from "lexical"

import type {CodeLanguage} from "../types"

/**
* Represents the serialized form of a CodeBlockNode.
* Extends SerializedElementNode with a language property.
*/
export type SerializedCodeBlockNode = Spread<
{
language: "json" | "yaml"
language: CodeLanguage
hasValidationError: boolean
},
SerializedElementNode
Expand All @@ -29,7 +31,7 @@ export type SerializedCodeBlockNode = Spread<
*/
export class CodeBlockNode extends ElementNode {
/** The programming language for syntax highlighting */
__language: "json" | "yaml"
__language: CodeLanguage
__hasValidationError: boolean

/**
Expand All @@ -54,7 +56,7 @@ export class CodeBlockNode extends ElementNode {
* @param language - The programming language for the code block (defaults to "json")
* @param key - Optional unique identifier for the node
*/
constructor(language: "json" | "yaml" = "json", hasValidationError?: boolean, key?: string) {
constructor(language: CodeLanguage = "json", hasValidationError?: boolean, key?: string) {
super(key)
this.__language = language
this.__hasValidationError = hasValidationError ?? false
Expand Down Expand Up @@ -111,11 +113,11 @@ export class CodeBlockNode extends ElementNode {
return false
}

getLanguage(): "json" | "yaml" {
getLanguage(): CodeLanguage {
return this.getLatest().__language
}

setLanguage(language: "json" | "yaml") {
setLanguage(language: CodeLanguage) {
const writable = this.getWritable()
writable.__language = language
}
Expand All @@ -139,7 +141,7 @@ export class CodeBlockNode extends ElementNode {
* @returns A new CodeBlockNode instance
*/
export function $createCodeBlockNode(
language: "json" | "yaml",
language: CodeLanguage,
hasValidationError?: boolean,
): CodeBlockNode {
return new CodeBlockNode(language, hasValidationError)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {useLexicalComposerContext} from "@lexical/react/LexicalComposerContext"
import {createPortal} from "react-dom"

import {$getActiveLanguage} from "../utils/language"
import type {CodeLanguage} from "../types"
import {validateAll} from "../utils/validationUtils"

import {$getEditorCodeAsString} from "./RealTimeValidationPlugin"
Expand Down Expand Up @@ -64,7 +65,7 @@ class ValidationManager {
validateContent(
content: string,
schema?: any,
language: "json" | "yaml" = "json",
language: CodeLanguage = "json",
): ValidationState {
// Skip if content hasn't changed
if (content === this.state.lastValidatedContent) {
Expand Down
3 changes: 3 additions & 0 deletions web/oss/src/components/Editor/plugins/code/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type CodeLanguage = "json" | "yaml" | "code"

export const DEFAULT_CODE_LANGUAGE: CodeLanguage = "json"
Loading