Skip to content

Commit

Permalink
Support setup global OpenAI settings. v5.15.11
Browse files Browse the repository at this point in the history
  • Loading branch information
winlinvip committed Jul 4, 2024
1 parent dc88df1 commit a16fb71
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 2 deletions.
3 changes: 3 additions & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,8 @@ Platform, with token authentication:
* `/terraform/v1/mgmt/beian/update` Update the beian information.
* `/terraform/v1/mgmt/limits/query` Query the limits information.
* `/terraform/v1/mgmt/limits/update` Update the limits information.
* `/terraform/v1/mgmt/openai/query` Query the OpenAI settings.
* `/terraform/v1/mgmt/openai/update` Update the OpenAI settings.
* `/terraform/v1/mgmt/secret/query` Query the api secret for OpenAPI.
* `/terraform/v1/mgmt/hphls/update` HLS delivery in high performance mode.
* `/terraform/v1/mgmt/hphls/query` Query HLS delivery in high performance mode.
Expand Down Expand Up @@ -1192,6 +1194,7 @@ The following are the update records for the Oryx server.
* Change LICENSE from AGPL-3.0-or-later to MIT. v5.15.8
* Dubbing: Support scrolling card in fullscreen. v5.15.9
* Support external redis host and using 127.0.0.1 as default. v5.15.10
* Support setup global OpenAI settings. [v5.15.11](https://github.com/ossrs/oryx/releases/tag/v5.15.11)
* v5.14:
* Merge features and bugfix from releases. v5.14.1
* Dubbing: Support VoD dubbing for multiple languages. [v5.14.2](https://github.com/ossrs/oryx/releases/tag/v5.14.2)
Expand Down
105 changes: 105 additions & 0 deletions platform/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,8 @@ func handleHTTPService(ctx context.Context, handler *http.ServeMux) error {
handleMgmtBilibili(ctx, handler)
handleMgmtLimitsQuery(ctx, handler)
handleMgmtLimitsUpdate(ctx, handler)
handleMgmtOpenAIQuery(ctx, handler)
handleMgmtOpenAIUpdate(ctx, handler)
handleMgmtBeianQuery(ctx, handler)
handleMgmtSecretQuery(ctx, handler)
handleMgmtBeianUpdate(ctx, handler)
Expand Down Expand Up @@ -927,6 +929,109 @@ func handleMgmtBilibili(ctx context.Context, handler *http.ServeMux) {
})
}

func handleMgmtOpenAIQuery(ctx context.Context, handler *http.ServeMux) {
ep := "/terraform/v1/mgmt/openai/query"
logger.Tf(ctx, "Handle %v", ep)
handler.HandleFunc(ep, func(w http.ResponseWriter, r *http.Request) {
if err := func() error {
var token string
if err := ParseBody(ctx, r.Body, &struct {
Token *string `json:"token"`
}{
Token: &token,
}); err != nil {
return errors.Wrapf(err, "parse body")
}

apiSecret := envApiSecret()
if err := Authenticate(ctx, apiSecret, token, r.Header); err != nil {
return errors.Wrapf(err, "authenticate")
}

aiSecretKey, err := rdb.HGet(ctx, SRS_SYS_OPENAI, "key").Result()
if err != nil && err != redis.Nil {
return errors.Wrapf(err, "hget %v key", SRS_SYS_OPENAI)
}

aiBaseURL, err := rdb.HGet(ctx, SRS_SYS_OPENAI, "url").Result()
if err != nil && err != redis.Nil {
return errors.Wrapf(err, "hget %v url", SRS_SYS_OPENAI)
}

aiOrganization, err := rdb.HGet(ctx, SRS_SYS_OPENAI, "org").Result()
if err != nil && err != redis.Nil {
return errors.Wrapf(err, "hget %v org", SRS_SYS_OPENAI)
}

ohttp.WriteData(ctx, w, r, &struct {
// The AI secret key.
AISecretKey string `json:"aiSecretKey"`
// The AI base url.
AIBaseURL string `json:"aiBaseURL"`
// The AI organization.
AIOrganization string `json:"aiOrganization"`
}{
AISecretKey: aiSecretKey, AIBaseURL: aiBaseURL, AIOrganization: aiOrganization,
})

logger.Tf(ctx, "settings: query openai ok")
return nil
}(); err != nil {
ohttp.WriteError(ctx, w, r, err)
}
})
}

func handleMgmtOpenAIUpdate(ctx context.Context, handler *http.ServeMux) {
ep := "/terraform/v1/mgmt/openai/update"
logger.Tf(ctx, "Handle %v", ep)
handler.HandleFunc(ep, func(w http.ResponseWriter, r *http.Request) {
if err := func() error {
var token string
var aiSecretKey, aiBaseURL, aiOrganization string
if err := ParseBody(ctx, r.Body, &struct {
Token *string `json:"token"`
AISecretKey *string `json:"aiSecretKey"`
AIBaseURL *string `json:"aiBaseURL"`
AIOrganization *string `json:"aiOrganization"`
}{
Token: &token, AISecretKey: &aiSecretKey, AIBaseURL: &aiBaseURL,
AIOrganization: &aiOrganization,
}); err != nil {
return errors.Wrapf(err, "parse body")
}

apiSecret := envApiSecret()
if err := Authenticate(ctx, apiSecret, token, r.Header); err != nil {
return errors.Wrapf(err, "authenticate")
}

if aiSecretKey == "" {
return errors.New("no aiSecretKey")
}
if aiBaseURL == "" {
return errors.New("no aiBaseURL")
}

if err := rdb.HSet(ctx, SRS_SYS_OPENAI, "key", aiSecretKey).Err(); err != nil && err != redis.Nil {
return errors.Wrapf(err, "hset %v key %v", SRS_SYS_OPENAI, aiSecretKey)
}
if err := rdb.HSet(ctx, SRS_SYS_OPENAI, "url", aiBaseURL).Err(); err != nil && err != redis.Nil {
return errors.Wrapf(err, "hset %v url %v", SRS_SYS_OPENAI, aiBaseURL)
}
if err := rdb.HSet(ctx, SRS_SYS_OPENAI, "org", aiOrganization).Err(); err != nil && err != redis.Nil {
return errors.Wrapf(err, "hset %v org %v", SRS_SYS_OPENAI, aiOrganization)
}

ohttp.WriteData(ctx, w, r, nil)
logger.Tf(ctx, "limits: Update ok, key=%vB, url=%v, org=%v", len(aiSecretKey), aiBaseURL, aiOrganization)
return nil
}(); err != nil {
ohttp.WriteError(ctx, w, r, err)
}
})
}

func handleMgmtLimitsQuery(ctx context.Context, handler *http.ServeMux) {
ep := "/terraform/v1/mgmt/limits/query"
logger.Tf(ctx, "Handle %v", ep)
Expand Down
1 change: 1 addition & 0 deletions platform/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ const (
SRS_HTTPS_DOMAIN = "SRS_HTTPS_DOMAIN"
SRS_HOOKS = "SRS_HOOKS"
SRS_SYS_LIMITS = "SRS_SYS_LIMITS"
SRS_SYS_OPENAI = "SRS_SYS_OPENAI"
)

// GenerateRoomPublishKey to build the redis hashset key from room stream name.
Expand Down
20 changes: 19 additions & 1 deletion ui/src/pages/ScenarioDubbing.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,11 @@ function ScenarioDubbingImpl({dubbingId, setDubbingId}) {

function DubbingSettings({project, requesting, updateProject}) {
const {t} = useTranslation();
const handleError = useErrorHandler();
const language = useSrsLanguage();
const [name, setName] = React.useState(project.title);
const [configItem, setConfigItem] = React.useState('asr');
const [loading, setLoading] = React.useState(true);

const [aiSecretKey, setAiSecretKey] = React.useState();
const [aiBaseURL, setAiBaseURL] = React.useState();
Expand Down Expand Up @@ -319,7 +321,23 @@ function DubbingSettings({project, requesting, updateProject}) {
setAiBaseURL(obj.aiBaseURL || (language === 'zh' ? '' : 'https://api.openai.com/v1'));
setAiOrganization(obj.aiOrganization);
}
}, [language, project, setAiSecretKey, setAiBaseURL, setAiOrganization]);

setLoading(false);
}, [language, project, setAiSecretKey, setAiBaseURL, setAiOrganization, setLoading]);

React.useEffect(() => {
if (loading || aiSecretKey) return;

axios.post('/terraform/v1/mgmt/openai/query', null, {
headers: Token.loadBearerHeader(),
}).then(res => {
const data = res.data.data;
setAiSecretKey(data.aiSecretKey);
setAiBaseURL(data.aiBaseURL);
setAiOrganization(data.aiOrganization);
console.log(`Dubbing: Query open ai ok, data=${JSON.stringify(data)}`);
}).catch(handleError);
}, [handleError, loading, aiSecretKey, setAiSecretKey, setAiBaseURL, setAiOrganization]);

const changeConfigItem = React.useCallback((e, t) => {
e.preventDefault();
Expand Down
15 changes: 15 additions & 0 deletions ui/src/pages/ScenarioLiveRoom.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ function LiveRoomStreamer({room}) {

function LiveRoomAssistant({room, requesting, updateRoom}) {
const {t} = useTranslation();
const handleError = useErrorHandler();
const language = useSrsLanguage();

const [aiName, setAiName] = React.useState(room.aiName);
Expand Down Expand Up @@ -441,6 +442,20 @@ function LiveRoomAssistant({room, requesting, updateRoom}) {
const [aiPattern, setAiPattern] = React.useState('chat');
const [assistantLink, setAssistantLink] = React.useState();

React.useEffect(() => {
if (aiSecretKey) return;

axios.post('/terraform/v1/mgmt/openai/query', null, {
headers: Token.loadBearerHeader(),
}).then(res => {
const data = res.data.data;
setAiSecretKey(data.aiSecretKey);
setAiBaseURL(data.aiBaseURL);
setAiOrganization(data.aiOrganization);
console.log(`LiveRoom: Query open ai ok, data=${JSON.stringify(data)}`);
}).catch(handleError);
}, [handleError, aiSecretKey, setAiSecretKey, setAiBaseURL, setAiOrganization]);

const changeConfigItem = React.useCallback((e, t) => {
e.preventDefault();
setConfigItem(t);
Expand Down
14 changes: 14 additions & 0 deletions ui/src/pages/ScenarioOCR.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ function ScenarioOCRImpl({activeKey, defaultEnabled, defaultConf, defaultUuid})
setConfigItem(t);
}, [setConfigItem]);

React.useEffect(() => {
if (aiSecretKey) return;

axios.post('/terraform/v1/mgmt/openai/query', null, {
headers: Token.loadBearerHeader(),
}).then(res => {
const data = res.data.data;
setAiSecretKey(data.aiSecretKey);
setAiBaseURL(data.aiBaseURL);
setAiOrganization(data.aiOrganization);
console.log(`OCR: Query open ai ok, data=${JSON.stringify(data)}`);
}).catch(handleError);
}, [handleError, aiSecretKey, setAiSecretKey, setAiBaseURL, setAiOrganization]);

const updateOcrService = React.useCallback((enabled, success) => {
if (!aiSecretKey) return alert(`Invalid secret key ${aiSecretKey}`);
if (!aiBaseURL) return alert(`Invalid base url ${aiBaseURL}`);
Expand Down
14 changes: 14 additions & 0 deletions ui/src/pages/ScenarioTranscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,20 @@ function ScenarioTranscriptImpl({activeKey, defaultEnabled, defaultConf, default

const [configItem, setConfigItem] = React.useState('provider');

React.useEffect(() => {
if (secretKey) return;

axios.post('/terraform/v1/mgmt/openai/query', null, {
headers: Token.loadBearerHeader(),
}).then(res => {
const data = res.data.data;
setSecretKey(data.aiSecretKey);
setBaseURL(data.aiBaseURL);
setOrganization(data.aiOrganization);
console.log(`Transcript: Query open ai ok, data=${JSON.stringify(data)}`);
}).catch(handleError);
}, [handleError, secretKey, setSecretKey, setBaseURL, setOrganization]);

const changeConfigItem = React.useCallback((e, t) => {
e.preventDefault();
setConfigItem(t);
Expand Down
61 changes: 60 additions & 1 deletion ui/src/pages/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {useTranslation} from "react-i18next";
import {TutorialsButton, useTutorials} from "../components/TutorialsButton";
import moment from "moment";
import PopoverConfirm from "../components/PopoverConfirm";
import {OpenAISecretSettings} from "../components/OpenAISettings";

export default function Systems() {
return (
Expand All @@ -29,7 +30,7 @@ function SystemsImpl() {

React.useEffect(() => {
const tab = searchParams.get('tab') || 'auth';
console.log(`?tab=https|hls|auth|beian|limits|callback|platform, current=${tab}, Select the tab to render`);
console.log(`?tab=https|hls|auth|beian|limits|llm|callback|platform, current=${tab}, Select the tab to render`);
setDefaultActiveTab(tab);
}, [searchParams]);

Expand Down Expand Up @@ -81,6 +82,9 @@ function SettingsImpl2({defaultActiveTab}) {
<Tab eventKey="limits" title={t('settings.tabLimits')}>
<SettingLimits />
</Tab>
<Tab eventKey="llm" title={t('settings.tabLLM')}>
<SettingLLM />
</Tab>
<Tab eventKey="streams" title={t('settings.tabStreams')}>
<SettingStreams />
</Tab>
Expand Down Expand Up @@ -492,6 +496,61 @@ function SettingStreams() {
);
}

function SettingLLM() {
const {t} = useTranslation();
const handleError = useErrorHandler();

const [aiSecretKey, setAiSecretKey] = React.useState();
const [aiBaseURL, setAiBaseURL] = React.useState();
const [aiOrganization, setAiOrganization] = React.useState();

React.useEffect(() => {
axios.post('/terraform/v1/mgmt/openai/query', null, {
headers: Token.loadBearerHeader(),
}).then(res => {
const data = res.data.data;
setAiSecretKey(data.aiSecretKey);
setAiBaseURL(data.aiBaseURL);
setAiOrganization(data.aiOrganization);
console.log(`Setting: Query open ai ok, data=${JSON.stringify(data)}`);
}).catch(handleError);
}, [handleError, setAiSecretKey, setAiBaseURL, setAiOrganization]);

const updateOpenAI = React.useCallback((e) => {
e.preventDefault();

axios.post('/terraform/v1/mgmt/openai/update', {
aiSecretKey, aiBaseURL, aiOrganization,
}, {
headers: Token.loadBearerHeader(),
}).then(res => {
alert(t('helper.setOk'));
console.log(`Setting: Update open ai ok`);
}).catch(handleError);
}, [handleError, aiSecretKey, aiBaseURL, aiOrganization]);

return <>
<Accordion defaultActiveKey={["1"]} alwaysOpen>
<Accordion.Item eventKey="1">
<Accordion.Header>{t('settings.openaiTitle')}</Accordion.Header>
<Accordion.Body>
<Form>
<OpenAISecretSettings {...{
baseURL: aiBaseURL, setBaseURL: setAiBaseURL,
secretKey: aiSecretKey, setSecretKey: setAiSecretKey,
organization: aiOrganization, setOrganization: setAiOrganization,
}} />
<p></p>
<Button variant="primary" type="submit" onClick={(e) => updateOpenAI(e)}>
{t('helper.submit')}
</Button>
</Form>
</Accordion.Body>
</Accordion.Item>
</Accordion>
</>;
}

function SettingLimits() {
const handleError = useErrorHandler();
const {t} = useTranslation();
Expand Down
4 changes: 4 additions & 0 deletions ui/src/resources/locale.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"tabLimit": "限流",
"tabFooter": "网站",
"tabLimits": "限流",
"tabLLM": "大模型",
"tabStreams": "流管理",
"tabCallback": "回调",
"tabTool": "工具",
Expand All @@ -106,6 +107,7 @@
"limitsVLive": "虚拟直播码率",
"limitsCamera": "摄像头直播码率",
"limitsTitle": "限流设置",
"openaiTitle": "OpenAI设置",
"activeStreams": "活跃流列表",
"footerTitle": "设置页脚(备案信息)",
"footerIcp": "ICP备案号",
Expand Down Expand Up @@ -1172,6 +1174,7 @@
"tabLimit": "Limit",
"tabFooter": "Website",
"tabLimits": "Limits",
"tabLLM": "LLM",
"tabStreams": "Streams",
"tabCallback": "Callback",
"tabTool": "Tools",
Expand All @@ -1196,6 +1199,7 @@
"limitsVLive": "Virtual Live Event Bitrate",
"limitsCamera": "Camera Streaming Bitrate",
"limitsTitle": "Set Limits",
"openaiTitle": "OpenAI Settings",
"activeStreams": "Active Streams",
"footerTitle": "Set Footer",
"footerIcp": "Footer",
Expand Down

0 comments on commit a16fb71

Please sign in to comment.